diff --git a/examples/htlc_logicsig/test_signature.py b/examples/htlc_logicsig/test_signature.py index 0317a6d1..b5ef0fcb 100644 --- a/examples/htlc_logicsig/test_signature.py +++ b/examples/htlc_logicsig/test_signature.py @@ -1,7 +1,6 @@ from collections.abc import Generator import algopy -import algosdk import pytest from algopy_testing import AlgopyTestContext, algopy_testing_context @@ -21,8 +20,8 @@ def test_seller_receives_payment(context: AlgopyTestContext) -> None: context.any.txn.payment( fee=algopy.UInt64(500), first_valid=algopy.UInt64(1000), - close_remainder_to=algopy.Account(algosdk.constants.ZERO_ADDRESS), - rekey_to=algopy.Account(algosdk.constants.ZERO_ADDRESS), + close_remainder_to=algopy.Account(), + rekey_to=algopy.Account(), receiver=algopy.Account( "6ZHGHH5Z5CTPCF5WCESXMGRSVK7QJETR63M3NY5FJCUYDHO57VTCMJOBGY" ), diff --git a/examples/proof_of_attendance/test_contract.py b/examples/proof_of_attendance/test_contract.py index c3a4e6fd..abcedaff 100644 --- a/examples/proof_of_attendance/test_contract.py +++ b/examples/proof_of_attendance/test_contract.py @@ -1,7 +1,6 @@ from collections.abc import Generator import algopy -import algosdk import pytest from algopy_testing import AlgopyTestContext, algopy_testing_context @@ -81,8 +80,8 @@ def test_claim_poa( opt_in_txn = context.any.txn.asset_transfer( sender=context.default_sender, asset_receiver=context.default_sender, - asset_close_to=algopy.Account(algosdk.constants.ZERO_ADDRESS), - rekey_to=algopy.Account(algosdk.constants.ZERO_ADDRESS), + asset_close_to=algopy.Account(), + rekey_to=algopy.Account(), xfer_asset=dummy_poa, fee=algopy.UInt64(0), asset_amount=algopy.UInt64(0), diff --git a/pyproject.toml b/pyproject.toml index 46af699c..766d999a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,17 +20,12 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] -dependencies = [ - # ========================================================================== - # Below is used for implementing algopy crypto ops matching AVM. - # Versions pinned for compatibility with py-algorand-sdk pre reqs - # https://github.com/algorand/py-algorand-sdk/blob/master/setup.py - # ========================================================================== +dependencies = [ "pycryptodomex>=3.6.0,<4", - "pynacl>=1.4.0,<2", "ecdsa>=0.17.0", "coincurve>=19.0.1", - "algorand-python>=3", + "algorand-python>=3", + "algokit-utils>=5.0.0a5", ] [project.urls] @@ -54,8 +49,6 @@ dependencies = [ "pytest>=7.4", "pytest-mock>=3.10.0", "pytest-xdist[psutil]>=3.3", - "py-algorand-sdk>=2.4.0", - "algokit-utils>=4.0.0", "pytest-cov>=4.1.0", "prettytable>=3.9.0", "mypy==1.10", @@ -130,8 +123,6 @@ dependencies = [ "pytest-mock>=3.10.0", "pytest-xdist[psutil]>=3.3", "pytest-cov>=4.1.0", - "py-algorand-sdk>=2.4.0", - "algokit-utils>=4.0.0", "puyapy>=5", ] @@ -157,7 +148,6 @@ dependencies = [ "sphinx-mermaid", "ipykernel", "pytest", - "py-algorand-sdk", ] # environment has algopy_testing included as an editable dependency # however it also includes the package dependencies @@ -183,12 +173,9 @@ post-install-commands = [ "hatch run examples:reload_algopy_testing", ] dependencies = [ - "algorand-python>=3", "pytest>=7.4", "pytest-mock>=3.10.0", "pytest-xdist[psutil]>=3.3", - "py-algorand-sdk>=2.4.0", - "algokit-utils>=4.0.0", "pytest-cov>=4.1.0", "mypy==1.10", ] @@ -276,7 +263,7 @@ ignore = [ "RET503", # false negatives when involving typing.Never, covered by mypy anyway "RET504", "RET505", # stylistic choices for readability - "S101", # allow asserts + "S101", # allow asserts "C901", # allow >10 args in a method "N805", # allow using `cls` as a firstparameter name ] @@ -317,15 +304,13 @@ builtins-ignorelist = ["id"] [tool.mypy] python_version = "3.12" strict = true -untyped_calls_exclude = [ - "algosdk", -] files = ["src", "tests", "examples"] exclude = ["tests/artifacts"] [[tool.mypy.overrides]] module = [ "tests.artifacts.*", + "rich.*", ] follow_imports = "skip" diff --git a/src/_algopy_testing/arc4.py b/src/_algopy_testing/arc4.py index 0917ec6a..fcaff1f5 100644 --- a/src/_algopy_testing/arc4.py +++ b/src/_algopy_testing/arc4.py @@ -6,7 +6,7 @@ import types import typing -import algosdk +from algokit_utils.common import ZERO_ADDRESS, public_key_from_address from Cryptodome.Hash import SHA512 from typing_extensions import deprecated @@ -751,11 +751,11 @@ def arc4_name(self) -> str: class Address(StaticArray[Byte, typing.Literal[32]]): _type_info = _AddressTypeInfo() - def __init__(self, value: Account | str | algopy.Bytes = algosdk.constants.ZERO_ADDRESS): + def __init__(self, value: Account | str | algopy.Bytes = ZERO_ADDRESS): super().__init__() if isinstance(value, str): try: - bytes_value = algosdk.encoding.decode_address(value) + bytes_value = public_key_from_address(value) except Exception as e: raise ValueError(f"cannot encode the following address: {value!r}") from e elif isinstance(value, Account): @@ -773,7 +773,7 @@ def native(self) -> Account: def __bool__(self) -> bool: # """Returns `True` if not equal to the zero address""" - zero_bytes: bytes = algosdk.encoding.decode_address(algosdk.constants.ZERO_ADDRESS) + zero_bytes: bytes = public_key_from_address(ZERO_ADDRESS) return self.bytes != zero_bytes def __eq__(self, other: Address | Account | str) -> bool: # type: ignore[override] @@ -782,7 +782,7 @@ def __eq__(self, other: Address | Account | str) -> bool: # type: ignore[overri if isinstance(other, Address | Account): return self.bytes == other.bytes elif isinstance(other, str): - other_bytes: bytes = algosdk.encoding.decode_address(other) + other_bytes: bytes = public_key_from_address(other) return self.bytes == other_bytes else: return NotImplemented diff --git a/src/_algopy_testing/constants.py b/src/_algopy_testing/constants.py index 7529968b..5c7607c9 100644 --- a/src/_algopy_testing/constants.py +++ b/src/_algopy_testing/constants.py @@ -28,3 +28,5 @@ b"\x09" # pragma version 9 b"\x81\x01" # pushint 1 ) + +LOGIC_DATA_PREFIX = b"ProgData" diff --git a/src/_algopy_testing/context.py b/src/_algopy_testing/context.py index cb0e6342..525b7a45 100644 --- a/src/_algopy_testing/context.py +++ b/src/_algopy_testing/context.py @@ -2,9 +2,8 @@ import typing -import algosdk - from _algopy_testing.context_helpers import LedgerContext, TransactionContext +from _algopy_testing.utils import generate_random_account from _algopy_testing.value_generators import AlgopyValueGenerator if typing.TYPE_CHECKING: @@ -33,9 +32,7 @@ def __init__( ) -> None: import algopy - self._default_sender = algopy.Account( - default_sender or algosdk.account.generate_account()[1] - ) + self._default_sender = algopy.Account(default_sender or generate_random_account().addr) self._template_vars: dict[str, typing.Any] = template_vars or {} self._active_lsig_args: Sequence[algopy.Bytes] = () diff --git a/src/_algopy_testing/context_helpers/ledger_context.py b/src/_algopy_testing/context_helpers/ledger_context.py index 903c264b..d9569e47 100644 --- a/src/_algopy_testing/context_helpers/ledger_context.py +++ b/src/_algopy_testing/context_helpers/ledger_context.py @@ -3,7 +3,7 @@ import typing from collections import defaultdict -import algosdk.constants +from algokit_utils.common import ZERO_ADDRESS from _algopy_testing.constants import MAX_BOX_SIZE from _algopy_testing.models.account import Account @@ -374,9 +374,9 @@ def set_block( # noqa: PLR0913 timestamp: algopy.UInt64 | int, bonus: algopy.UInt64 | int = 0, branch: algopy.Bytes | bytes = b"", - fee_sink: algopy.Account | str = algosdk.constants.ZERO_ADDRESS, + fee_sink: algopy.Account | str = ZERO_ADDRESS, fees_collected: algopy.UInt64 | int = 0, - proposer: algopy.Account | str = algosdk.constants.ZERO_ADDRESS, + proposer: algopy.Account | str = ZERO_ADDRESS, proposer_payout: algopy.UInt64 | int = 0, protocol: algopy.Bytes | bytes = b"", txn_counter: algopy.UInt64 | int = 0, diff --git a/src/_algopy_testing/context_helpers/txn_context.py b/src/_algopy_testing/context_helpers/txn_context.py index e2ab831c..a555ca37 100644 --- a/src/_algopy_testing/context_helpers/txn_context.py +++ b/src/_algopy_testing/context_helpers/txn_context.py @@ -4,7 +4,7 @@ import time import typing -import algosdk +from algokit_utils.transactions import MAX_TRANSACTION_GROUP_SIZE from _algopy_testing import gtxn from _algopy_testing.decorators.arc4 import ( @@ -229,9 +229,9 @@ def _set_txn_group( if not all(isinstance(txn, gtxn.TransactionBase) for txn in txns): raise ValueError("All transactions must be instances of TransactionBase") - if len(txns) > algosdk.constants.TX_GROUP_LIMIT: + if len(txns) > MAX_TRANSACTION_GROUP_SIZE: raise ValueError( - f"Transaction group can have at most {algosdk.constants.TX_GROUP_LIMIT} " + f"Transaction group can have at most {MAX_TRANSACTION_GROUP_SIZE} " "transactions, as per AVM limits." ) diff --git a/src/_algopy_testing/decorators/arc4.py b/src/_algopy_testing/decorators/arc4.py index b886e39c..8fe79f37 100644 --- a/src/_algopy_testing/decorators/arc4.py +++ b/src/_algopy_testing/decorators/arc4.py @@ -6,7 +6,7 @@ import types import typing -import algosdk +from algokit_utils.applications.abi import Arc56Method import _algopy_testing from _algopy_testing.constants import ALWAYS_APPROVE_TEAL_PROGRAM, ARC4_RETURN_PREFIX @@ -217,7 +217,7 @@ def create_abimethod_txns( contract_app = lazy_context.ledger.get_app(app_id) txn_fields = get_active_txn_fields(contract_app, allow_actions) - method = algosdk.abi.Method.from_signature(arc4_signature) + method = Arc56Method.from_signature(arc4_signature) method_selector = Bytes(method.get_selector()) txn_arrays = _extract_arrays_from_args( args, @@ -337,18 +337,11 @@ def _generate_arc4_signature_from_fn( fn: typing.Callable[_P, _R], arc4_name: str, resource_encoding: _ResourceEncoding ) -> str: annotations = inspect.get_annotations(fn, eval_str=True).copy() - returns = algosdk.abi.Returns( - _type_to_arc4(annotations.pop("return"), resource_encoding, "output") - ) - method = algosdk.abi.Method( - name=arc4_name, - args=[ - algosdk.abi.Argument(_type_to_arc4(a, resource_encoding, "input")) - for a in annotations.values() - ], - returns=returns, - ) - return method.get_signature() + returns = _type_to_arc4(annotations.pop("return"), resource_encoding, "output") + + args = ",".join([_type_to_arc4(a, resource_encoding, "input") for a in annotations.values()]) + + return f"{arc4_name}({args}){returns}" def _type_to_arc4( # noqa: PLR0912 PLR0911 diff --git a/src/_algopy_testing/enums.py b/src/_algopy_testing/enums.py index 6d3a5548..3769ef9a 100644 --- a/src/_algopy_testing/enums.py +++ b/src/_algopy_testing/enums.py @@ -2,7 +2,7 @@ import typing -from algosdk import constants +from algokit_utils.transact import TransactionType as BaseTransactionType from _algopy_testing.primitives import UInt64 @@ -73,17 +73,17 @@ class TransactionType(_EnumLike): def txn_name(self) -> str: match self: case self.Payment: - return constants.PAYMENT_TXN + return BaseTransactionType.Payment.value case self.KeyRegistration: - return constants.KEYREG_TXN + return BaseTransactionType.KeyRegistration.value case self.AssetConfig: - return constants.ASSETCONFIG_TXN + return BaseTransactionType.AssetConfig.value case self.AssetTransfer: - return constants.ASSETTRANSFER_TXN + return BaseTransactionType.AssetTransfer.value case self.AssetFreeze: - return constants.ASSETFREEZE_TXN + return BaseTransactionType.AssetFreeze.value case self.ApplicationCall: - return constants.APPCALL_TXN + return BaseTransactionType.AppCall.value case _: raise ValueError("unexpected transaction type") diff --git a/src/_algopy_testing/itxn.py b/src/_algopy_testing/itxn.py index fbc26e8b..2f00d425 100644 --- a/src/_algopy_testing/itxn.py +++ b/src/_algopy_testing/itxn.py @@ -4,7 +4,7 @@ import typing from copy import deepcopy -import algosdk +from algokit_utils.transactions import MAX_TRANSACTION_GROUP_SIZE from _algopy_testing.context_helpers import lazy_context from _algopy_testing.enums import TransactionType @@ -168,7 +168,7 @@ class ApplicationCall(_BaseInnerTransactionFields[ApplicationCallInnerTransactio def submit_txns( *transactions: _BaseInnerTransactionFields[_TResult_co], ) -> tuple[_BaseInnerTransactionResult, ...]: - if len(transactions) > algosdk.constants.TX_GROUP_LIMIT: + if len(transactions) > MAX_TRANSACTION_GROUP_SIZE: raise ValueError("Cannot submit more than 16 inner transactions at once") results = tuple(_get_itxn_result(tx) for tx in transactions) diff --git a/src/_algopy_testing/models/account.py b/src/_algopy_testing/models/account.py index 73181bbd..3b98cb74 100644 --- a/src/_algopy_testing/models/account.py +++ b/src/_algopy_testing/models/account.py @@ -3,7 +3,7 @@ import dataclasses import typing -import algosdk +from algokit_utils.common import ZERO_ADDRESS, address_from_public_key, public_key_from_address from _algopy_testing.constants import DEFAULT_ACCOUNT_MIN_BALANCE from _algopy_testing.primitives import Bytes, UInt64 @@ -79,12 +79,12 @@ class AccountContextData: class Account(BytesBacked): - def __init__(self, value: str | Bytes = algosdk.constants.ZERO_ADDRESS, /): + def __init__(self, value: str | Bytes = ZERO_ADDRESS, /): if not isinstance(value, str | Bytes): raise TypeError("Invalid value for Account") self._public_key: bytes = ( - algosdk.encoding.decode_address(value) if isinstance(value, str) else value.value + public_key_from_address(value) if isinstance(value, str) else value.value ) @property @@ -126,7 +126,7 @@ def bytes(self) -> Bytes: @property def public_key(self) -> str: - return algosdk.encoding.encode_address(self._public_key) # type: ignore[no-any-return] + return address_from_public_key(self._public_key) def validate(self) -> None: pass @@ -151,9 +151,7 @@ def __eq__(self, other: object) -> bool: return NotImplemented def __bool__(self) -> bool: - return bool(self._public_key) and self._public_key != algosdk.encoding.decode_address( - algosdk.constants.ZERO_ADDRESS - ) + return bool(self._public_key) and self._public_key != public_key_from_address(ZERO_ADDRESS) def __hash__(self) -> int: return hash(self._public_key) diff --git a/src/_algopy_testing/models/application.py b/src/_algopy_testing/models/application.py index 0c096e80..8b1a6965 100644 --- a/src/_algopy_testing/models/application.py +++ b/src/_algopy_testing/models/application.py @@ -2,7 +2,7 @@ import typing -import algosdk.logic +from algokit_utils.common import get_application_address from _algopy_testing.primitives import UInt64 from _algopy_testing.protocols import UInt64Backed @@ -63,7 +63,7 @@ def id(self) -> algopy.UInt64: def address(self) -> algopy.Account: from _algopy_testing.models import Account - address = algosdk.logic.get_application_address(self._id) + address = get_application_address(self._id) return Account(address) @property diff --git a/src/_algopy_testing/op/crypto.py b/src/_algopy_testing/op/crypto.py index 48062d5e..44e4e523 100644 --- a/src/_algopy_testing/op/crypto.py +++ b/src/_algopy_testing/op/crypto.py @@ -3,12 +3,12 @@ import enum import hashlib import typing -from collections.abc import Sequence -import algosdk import coincurve import nacl.exceptions import nacl.signing +from algokit_utils.common import public_key_from_address +from algokit_utils.transact import LogicSigAccount from Cryptodome.Hash import SHA512, keccak from ecdsa import ( # type: ignore # noqa: PGH003 BadSignatureError, @@ -17,11 +17,15 @@ VerifyingKey, ) +from _algopy_testing.constants import LOGIC_DATA_PREFIX from _algopy_testing.context_helpers import lazy_context from _algopy_testing.enums import OnCompleteAction from _algopy_testing.primitives import Bytes, UInt64 from _algopy_testing.utils import as_bytes, raise_mocked_function_error +if typing.TYPE_CHECKING: + from collections.abc import Sequence + class ECDSA(enum.Enum): Secp256k1 = 0 @@ -76,7 +80,7 @@ def ed25519verify(a: Bytes | bytes, b: Bytes | bytes, c: Bytes | bytes, /) -> bo txn = lazy_context.active_group.active_txn program_pages = typing.cast( - Sequence[Bytes], + "Sequence[Bytes]", ( txn.fields["clear_state_program"] if txn.on_completion == OnCompleteAction.ClearState @@ -85,9 +89,9 @@ def ed25519verify(a: Bytes | bytes, b: Bytes | bytes, c: Bytes | bytes, /) -> bo ) program_bytes = b"".join(map(as_bytes, program_pages)) - decoded_address = algosdk.encoding.decode_address(algosdk.logic.address(program_bytes)) + decoded_address = public_key_from_address(LogicSigAccount(logic=program_bytes).address) address_bytes = as_bytes(decoded_address) - a = algosdk.constants.logic_data_prefix + address_bytes + a + a = LOGIC_DATA_PREFIX + address_bytes + a return ed25519verify_bare(a, b, c) @@ -195,7 +199,6 @@ class EC(enum.StrEnum): class _MockedMember: - def __set_name__(self, owner: type, name: str) -> None: self.name = f"{owner.__name__}.{name}" diff --git a/src/_algopy_testing/op/global_values.py b/src/_algopy_testing/op/global_values.py index 6caa1618..365dbfb3 100644 --- a/src/_algopy_testing/op/global_values.py +++ b/src/_algopy_testing/op/global_values.py @@ -3,7 +3,7 @@ import typing from typing import TypedDict, TypeVar -import algosdk +from algokit_utils.common import ZERO_ADDRESS, get_application_address from _algopy_testing.context_helpers import lazy_context from _algopy_testing.models import Account @@ -45,7 +45,7 @@ def _fields(self) -> GlobalFields: @property def current_application_address(self) -> algopy.Account: - app_address = algosdk.logic.get_application_address(int(self.current_application_id.id)) + app_address = get_application_address(int(self.current_application_id.id)) return Account(app_address) @property @@ -58,7 +58,7 @@ def caller_application_id(self) -> algopy.UInt64: @property def caller_application_address(self) -> algopy.Account: - app_address = algosdk.logic.get_application_address(int(self.caller_application_id)) + app_address = get_application_address(int(self.caller_application_id)) return Account(app_address) @property @@ -104,7 +104,7 @@ def zero_address(self) -> algopy.Account: try: return self._fields["zero_address"] except KeyError: - return Account(algosdk.constants.ZERO_ADDRESS) + return Account(ZERO_ADDRESS) def __getattr__(self, name: str) -> typing.Any: try: diff --git a/src/_algopy_testing/utils.py b/src/_algopy_testing/utils.py index 49fdb817..0a16800c 100644 --- a/src/_algopy_testing/utils.py +++ b/src/_algopy_testing/utils.py @@ -5,7 +5,9 @@ import typing from typing import TYPE_CHECKING -import algosdk +from algokit_utils import AddressWithSigners, AlgorandClient, ClientManager +from algokit_utils.accounts import AccountManager +from algokit_utils.common import MIN_TXN_FEE, ZERO_ADDRESS, public_key_from_address import _algopy_testing from _algopy_testing.constants import ( @@ -60,6 +62,14 @@ def generate_random_bytes32() -> bytes: return secrets.token_bytes(32) +def generate_random_account() -> AddressWithSigners: + """Generate a new random Algorand account and return its address.""" + configs = ClientManager.get_config_from_environment_or_localnet() + client_manager = ClientManager(configs, AlgorandClient(configs)) + acc = AccountManager(client_manager).random() + return acc + + def as_int(value: object, *, max: int | None) -> int: # noqa: A002 match value: case int(int_value): @@ -157,17 +167,24 @@ def check_type(value: object, typ: type | types.UnionType) -> None: def assert_address_is_valid(address: str) -> None: - assert algosdk.encoding.is_valid_address(address), "Invalid Algorand address supplied!" + def is_valid_algorand_address(address: str) -> bool: + try: + public_key_from_address(address) + except: # noqa: E722 + return False + return True + + assert is_valid_algorand_address(address), "Invalid Algorand address supplied!" def get_default_global_fields() -> GlobalFields: import algopy return { - "min_txn_fee": algopy.UInt64(algosdk.constants.MIN_TXN_FEE), + "min_txn_fee": algopy.UInt64(MIN_TXN_FEE), "min_balance": algopy.UInt64(DEFAULT_ACCOUNT_MIN_BALANCE), "max_txn_life": algopy.UInt64(DEFAULT_MAX_TXN_LIFE), - "zero_address": algopy.Account(algosdk.constants.ZERO_ADDRESS), + "zero_address": algopy.Account(ZERO_ADDRESS), "caller_application_id": algopy.UInt64(), "asset_create_min_balance": algopy.UInt64(DEFAULT_ASSET_CREATE_MIN_BALANCE), "asset_opt_in_min_balance": algopy.UInt64(DEFAULT_ASSET_OPT_IN_MIN_BALANCE), diff --git a/src/_algopy_testing/value_generators/arc4.py b/src/_algopy_testing/value_generators/arc4.py index bacca57c..6b2cd7cc 100644 --- a/src/_algopy_testing/value_generators/arc4.py +++ b/src/_algopy_testing/value_generators/arc4.py @@ -4,11 +4,9 @@ import string import typing -import algosdk - from _algopy_testing import arc4 from _algopy_testing.constants import MAX_UINT8, MAX_UINT16, MAX_UINT32, MAX_UINT64, MAX_UINT512 -from _algopy_testing.utils import generate_random_int +from _algopy_testing.utils import generate_random_account, generate_random_int if typing.TYPE_CHECKING: import algopy @@ -23,7 +21,8 @@ def address(self) -> algopy.arc4.Address: :returns: A new, random Algorand address. """ - return arc4.Address(algosdk.account.generate_account()[1]) + address = generate_random_account().addr + return arc4.Address(address) def uint8(self, min_value: int = 0, max_value: int = MAX_UINT8) -> algopy.arc4.UInt8: """Generate a random UInt8 within the specified range. diff --git a/src/_algopy_testing/value_generators/avm.py b/src/_algopy_testing/value_generators/avm.py index 0c46b716..ad3227ac 100644 --- a/src/_algopy_testing/value_generators/avm.py +++ b/src/_algopy_testing/value_generators/avm.py @@ -5,7 +5,7 @@ import typing from collections import ChainMap -import algosdk +from algokit_utils.common import ZERO_ADDRESS import _algopy_testing from _algopy_testing.constants import ( @@ -18,7 +18,7 @@ from _algopy_testing.models.account import AccountFields from _algopy_testing.models.application import ApplicationContextData, ApplicationFields from _algopy_testing.models.asset import AssetFields -from _algopy_testing.utils import generate_random_int +from _algopy_testing.utils import generate_random_account, generate_random_int if typing.TYPE_CHECKING: import algopy @@ -92,7 +92,7 @@ def account( raise AttributeError(f"Invalid field '{key}' for Account") ledger = lazy_context.ledger - new_account_address = address or algosdk.account.generate_account()[1] + new_account_address = address or generate_random_account().addr new_account = algopy.Account(new_account_address) # defaultdict of account_data ensures we get a new initialized account account_data = lazy_context.get_account_data(new_account_address) @@ -129,11 +129,11 @@ def asset( "name": lazy_context.any.bytes(32), "url": lazy_context.any.bytes(10), "metadata_hash": lazy_context.any.bytes(32), - "manager": algopy.Account(algosdk.constants.ZERO_ADDRESS), - "freeze": algopy.Account(algosdk.constants.ZERO_ADDRESS), - "clawback": algopy.Account(algosdk.constants.ZERO_ADDRESS), + "manager": algopy.Account(ZERO_ADDRESS), + "freeze": algopy.Account(ZERO_ADDRESS), + "clawback": algopy.Account(ZERO_ADDRESS), "creator": lazy_context.value.default_sender, - "reserve": algopy.Account(algosdk.constants.ZERO_ADDRESS), + "reserve": algopy.Account(ZERO_ADDRESS), } merged_fields = dict(ChainMap(asset_fields, default_asset_fields)) # type: ignore[arg-type] lazy_context.ledger._asset_data[int(new_asset.id)] = AssetFields(**merged_fields) # type: ignore[typeddict-item] diff --git a/tests/arc4/test_address.py b/tests/arc4/test_address.py index aae4cd3b..09bda163 100644 --- a/tests/arc4/test_address.py +++ b/tests/arc4/test_address.py @@ -1,10 +1,11 @@ import _algopy_testing.primitives as algopy -import algosdk import pytest from _algopy_testing import arc4 from _algopy_testing.models import Account +from algokit_utils import ABIType +from algokit_utils.common import ZERO_ADDRESS, address_from_public_key -_abi_address_type = algosdk.abi.ABIType.from_string("address") +_abi_address_type = ABIType.from_string("address") _test_values = [ b"\x00" * 32, @@ -29,7 +30,7 @@ def test_bytes_from_bytes(value: bytes) -> None: _test_values, ) def test_bytes_from_str(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) abi_result = _abi_address_type.encode(str_value) arc4_result = arc4.Address(str_value).bytes assert abi_result == arc4_result @@ -51,9 +52,9 @@ def test_bytes_from_account(value: bytes) -> None: _test_values, ) def test_bool_from_bytes(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) arc4_result = bool(arc4.Address(algopy.Bytes(value))) - assert arc4_result == (str_value != algosdk.constants.ZERO_ADDRESS) + assert arc4_result == (str_value != ZERO_ADDRESS) @pytest.mark.parametrize( @@ -61,9 +62,9 @@ def test_bool_from_bytes(value: bytes) -> None: _test_values, ) def test_bool_from_str(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) arc4_result = bool(arc4.Address(str_value)) - assert arc4_result == (str_value != algosdk.constants.ZERO_ADDRESS) + assert arc4_result == (str_value != ZERO_ADDRESS) @pytest.mark.parametrize( @@ -71,10 +72,10 @@ def test_bool_from_str(value: bytes) -> None: _test_values, ) def test_bool_from_account(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) account = Account(algopy.Bytes(value)) arc4_result = bool(arc4.Address(account)) - assert arc4_result == (str_value != algosdk.constants.ZERO_ADDRESS) + assert arc4_result == (str_value != ZERO_ADDRESS) @pytest.mark.parametrize( @@ -94,7 +95,7 @@ def test_copy_from_bytes(value: bytes) -> None: _test_values, ) def test_copy_from_str(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) abi_result = _abi_address_type.encode(str_value) arc4_value = arc4.Address(str_value) copy = arc4_value.copy() @@ -133,7 +134,7 @@ def test_get_item_from_bytes(value: bytes) -> None: ) def test_get_item_from_str(value: bytes) -> None: input_bytes = algopy.Bytes(value) - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) arc4_value = arc4.Address(str_value) for idx, x in enumerate(arc4_value): assert x.bytes == input_bytes[idx].value @@ -159,7 +160,7 @@ def test_get_item_from_account(value: bytes) -> None: ) def test_comparison_with_self_from_bytes(value: bytes) -> None: arc4_value = arc4.Address(algopy.Bytes(value)) - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) assert arc4_value == str_value account = Account(algopy.Bytes(value)) @@ -173,7 +174,7 @@ def test_comparison_with_self_from_bytes(value: bytes) -> None: _test_values, ) def test_comparison_with_self_from_str(value: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) arc4_value = arc4.Address(str_value) assert arc4_value == str_value @@ -192,7 +193,7 @@ def test_comparison_with_self_from_account(value: bytes) -> None: arc4_value = arc4.Address(account) assert arc4_value == account - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) assert arc4_value == str_value assert arc4_value == arc4.Address(algopy.Bytes(value)) @@ -205,7 +206,7 @@ def test_comparison_with_self_from_account(value: bytes) -> None: def test_comparison_with_other_from_bytes(value: bytes, other: bytes) -> None: arc4_value = arc4.Address(algopy.Bytes(value)) - other_str_value = algosdk.encoding.encode_address(other) + other_str_value = address_from_public_key(other) assert arc4_value != other_str_value other_account = Account(algopy.Bytes(other)) @@ -219,10 +220,10 @@ def test_comparison_with_other_from_bytes(value: bytes, other: bytes) -> None: zip(_test_values, reversed(_test_values), strict=False), ) def test_comparison_with_other_from_str(value: bytes, other: bytes) -> None: - str_value = algosdk.encoding.encode_address(value) + str_value = address_from_public_key(value) arc4_value = arc4.Address(str_value) - other_str_value = algosdk.encoding.encode_address(other) + other_str_value = address_from_public_key(other) assert arc4_value != other_str_value other_account = Account(algopy.Bytes(other)) @@ -239,7 +240,7 @@ def test_comparison_with_other_from_account(value: bytes, other: bytes) -> None: account = Account(algopy.Bytes(value)) arc4_value = arc4.Address(account) - other_str_value = algosdk.encoding.encode_address(other) + other_str_value = address_from_public_key(other) assert arc4_value != other_str_value other_account = Account(algopy.Bytes(other)) diff --git a/tests/arc4/test_arc4_method_signature.py b/tests/arc4/test_arc4_method_signature.py index eaf53520..a962a725 100644 --- a/tests/arc4/test_arc4_method_signature.py +++ b/tests/arc4/test_arc4_method_signature.py @@ -5,11 +5,10 @@ import _algopy_testing import algokit_utils import algopy -import algosdk import pytest from algokit_utils import AlgoAmount, AlgorandClient, AssetCreateParams, PaymentParams +from algokit_utils.applications.abi import Arc56Method from algopy import arc4 -from algosdk.atomic_transaction_composer import TransactionWithSigner from tests.artifacts.Arc4ABIMethod.contract import ( AnotherStruct, @@ -43,10 +42,8 @@ def funded_account(algorand: AlgorandClient, context: _algopy_testing.AlgopyTest min_spending_balance=algokit_utils.AlgoAmount(micro_algo=_FUNDED_ACCOUNT_SPENDING), ) # ensure context has the same account with matching balance - context.any.account( - account.address, balance=algopy.Global.min_balance + _FUNDED_ACCOUNT_SPENDING - ) - return account.address + context.any.account(account.addr, balance=algopy.Global.min_balance + _FUNDED_ACCOUNT_SPENDING) + return account.addr @pytest.fixture() @@ -83,7 +80,7 @@ def test_app_args_is_correct_with_simple_args( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature("sink(string,uint8[],byte[4])void").get_selector(), + Arc56Method.from_signature("sink(string,uint8[],byte[4])void").get_selector(), b"\x00\x05hello", b"\x00\x02\x01\x02", b"test", @@ -109,7 +106,7 @@ def test_app_args_is_correct_with_alias( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature("alias(string,uint8[])void").get_selector(), + Arc56Method.from_signature("alias(string,uint8[])void").get_selector(), b"\x00\x05hello", b"\x00\x02\x01\x02", ] @@ -130,15 +127,13 @@ def test_app_args_is_correct_with_txn( get_avm_result( "with_txn", value="hello", - pay=TransactionWithSigner( - txn=algorand.create_transaction.payment( - PaymentParams( - sender=localnet_creator_address, - receiver=localnet_creator_address, - amount=AlgoAmount(micro_algo=123), - ) - ), - signer=algorand.account.get_signer(localnet_creator_address), + pay=algorand.create_transaction.payment( + PaymentParams( + sender=localnet_creator_address, + receiver=localnet_creator_address, + amount=AlgoAmount(micro_algo=123), + signer=algorand.account.get_signer(localnet_creator_address), + ) ), arr=[1, 2], ) @@ -152,7 +147,7 @@ def test_app_args_is_correct_with_txn( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(3)] assert app_args == [ - algosdk.abi.Method.from_signature("with_txn(string,pay,uint8[])void").get_selector(), + Arc56Method.from_signature("with_txn(string,pay,uint8[])void").get_selector(), b"\x00\x05hello", b"\x00\x02\x01\x02", ] @@ -174,9 +169,7 @@ def test_app_args_is_correct_with_asset( sender=localnet_creator_address, total=123, ) - ).confirmation[ - "asset-index" - ] # type: ignore[call-overload] + ).asset_id # act get_avm_result("with_asset", value="hello", asset=asa_id, arr=[1, 2]) @@ -190,7 +183,7 @@ def test_app_args_is_correct_with_asset( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature("with_asset(string,asset,uint8[])void").get_selector(), + Arc56Method.from_signature("with_asset(string,asset,uint8[])void").get_selector(), b"\x00\x05hello", b"\x00", b"\x00\x02\x01\x02", @@ -224,7 +217,7 @@ def test_app_args_is_correct_with_account( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature("with_acc(string,account,uint8[])void").get_selector(), + Arc56Method.from_signature("with_acc(string,account,uint8[])void").get_selector(), b"\x00\x05hello", b"\x01", b"\x00\x02\x01\x02", @@ -263,7 +256,7 @@ def test_app_args_is_correct_with_application( app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] app_foreign_apps = [txn.apps(i).id for i in range(int(txn.num_apps))] assert app_args == [ - algosdk.abi.Method.from_signature( + Arc56Method.from_signature( "with_app(string,application,uint64,uint8[])void" ).get_selector(), b"\x00\x05hello", @@ -303,7 +296,7 @@ def test_app_args_is_correct_with_complex( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature( + Arc56Method.from_signature( "complex_sig(((uint64,string),(uint64,string),uint128,uint128),txn,account,uint8[])((uint64,string),((uint64,string),(uint64,string),uint128,uint128))" ).get_selector(), b"\x00$\x001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\n\x00\x012\x00\x00\x00\x00\x00\x00\x00\x01\x00\n\x00\x012", @@ -343,15 +336,13 @@ def test_prepare_txns_with_complex( get_avm_result( "complex_sig", struct1=((1, "2"), (1, "2"), 3, 4), - txn=TransactionWithSigner( - txn=algorand.create_transaction.payment( - PaymentParams( - sender=localnet_creator_address, - receiver=localnet_creator_address, - amount=AlgoAmount(micro_algo=123), - ) + txn=algorand.create_transaction.payment( + PaymentParams( + sender=localnet_creator_address, + receiver=localnet_creator_address, + amount=AlgoAmount(micro_algo=123), + signer=algorand.account.get_signer(localnet_creator_address), ), - signer=algorand.account.get_signer(localnet_creator_address), ), acc=funded_account, five=[5], @@ -365,7 +356,7 @@ def test_prepare_txns_with_complex( txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature( + Arc56Method.from_signature( "complex_sig(((uint64,string),(uint64,string),uint128,uint128),txn,account,uint8[])((uint64,string),((uint64,string),(uint64,string),uint128,uint128))" ).get_selector(), b"\x00$\x001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\n\x00\x012\x00\x00\x00\x00\x00\x00\x00\x01\x00\n\x00\x012", @@ -394,9 +385,7 @@ def test_app_args_is_correct_with_index_resource_encoding( # noqa: PLR0913 sender=localnet_creator_address, total=123, ) - ).confirmation[ - "asset-index" - ] # type: ignore[call-overload] + ).asset_id # act contract.echo_resource_by_index( @@ -415,7 +404,7 @@ def test_app_args_is_correct_with_index_resource_encoding( # noqa: PLR0913 txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature( + Arc56Method.from_signature( "echo_resource_by_index(asset,application,account)(uint64,uint64,address)" ).get_selector(), b"\x00", @@ -424,7 +413,7 @@ def test_app_args_is_correct_with_index_resource_encoding( # noqa: PLR0913 ] assert app_args[0] == arc4.arc4_signature(SignaturesContract.echo_resource_by_index) - assert result == [asa_id, other_app_id, funded_account] + assert result == (asa_id, other_app_id, funded_account) def test_app_args_is_correct_with_value_resource_encoding( # noqa: PLR0913 @@ -444,9 +433,7 @@ def test_app_args_is_correct_with_value_resource_encoding( # noqa: PLR0913 sender=localnet_creator_address, total=123, ) - ).confirmation[ - "asset-index" - ] # type: ignore[call-overload] + ).asset_id asset = context.any.asset(asset_id=asa_id, total=algopy.UInt64(123)) app = context.ledger.get_app(other_app_id) @@ -468,7 +455,7 @@ def test_app_args_is_correct_with_value_resource_encoding( # noqa: PLR0913 txn = context.txn.last_active app_args = [txn.app_args(i) for i in range(int(txn.num_app_args))] assert app_args == [ - algosdk.abi.Method.from_signature( + Arc56Method.from_signature( "echo_resource_by_value(uint64,uint64,address)(uint64,uint64,address)" ).get_selector(), asa_id.to_bytes(length=8), # asset id as bytes @@ -477,4 +464,4 @@ def test_app_args_is_correct_with_value_resource_encoding( # noqa: PLR0913 ] assert app_args[0] == arc4.arc4_signature(SignaturesContract.echo_resource_by_value) - assert result == [asset.id, app.id, acc] + assert result == (asset.id, app.id, acc) diff --git a/tests/arc4/test_dynamic_array.py b/tests/arc4/test_dynamic_array.py index c561278d..36ab9afd 100644 --- a/tests/arc4/test_dynamic_array.py +++ b/tests/arc4/test_dynamic_array.py @@ -5,7 +5,7 @@ from _algopy_testing import arc4 from _algopy_testing.primitives import BigUInt, String, UInt64 from _algopy_testing.primitives.array import Array, ImmutableArray -from algosdk import abi +from algokit_utils.applications import abi from tests.util import int_to_bytes diff --git a/tests/arc4/test_dynamic_bytes.py b/tests/arc4/test_dynamic_bytes.py index 381e0960..9fc61d95 100644 --- a/tests/arc4/test_dynamic_bytes.py +++ b/tests/arc4/test_dynamic_bytes.py @@ -1,13 +1,13 @@ import copy import typing -import algosdk import pytest from _algopy_testing import Bytes, arc4 +from algokit_utils.applications import abi from tests.util import int_to_bytes -_abi_dynamic_bytes_type = algosdk.abi.ABIType.from_string("byte[]") +_abi_dynamic_bytes_type = abi.ABIType.from_string("byte[]") _abi_int_array_values = [0, 1, 8, 16, 32, 64, 128, 255, 20, 30, 40, 50, 111] _abi_bytes_value = b"".join([int_to_bytes(value) for value in _abi_int_array_values]) diff --git a/tests/arc4/test_emit.py b/tests/arc4/test_emit.py index 44f29cba..5ca5cb5d 100644 --- a/tests/arc4/test_emit.py +++ b/tests/arc4/test_emit.py @@ -1,4 +1,3 @@ -import base64 import typing from collections.abc import Generator @@ -83,7 +82,7 @@ def test_emit(get_avm_result: AVMInvoker, context: AlgopyTestContext) -> None: arc4.DynamicArray(arc4.UInt16(1), arc4.UInt16(2), arc4.UInt16(3)), arc4.Tuple((arc4.UInt32(1), arc4.UInt64(2), arc4.String("hello"))), ) - avm_result_ = get_avm_result( + avm_result = get_avm_result( "verify_emit", a=_test_data.a.value, b=_test_data.b, @@ -102,8 +101,7 @@ def test_emit(get_avm_result: AVMInvoker, context: AlgopyTestContext) -> None: s=_test_data_arc4.s.bytes.value, t=_test_data_arc4.t.bytes.value, ) - assert isinstance(avm_result_, list) - avm_result = [base64.b64decode(b) for b in avm_result_] + assert isinstance(avm_result, list) arc4.emit(_test_data_arc4) arc4.emit( diff --git a/tests/arc4/test_static_array.py b/tests/arc4/test_static_array.py index 36c0103e..1536f497 100644 --- a/tests/arc4/test_static_array.py +++ b/tests/arc4/test_static_array.py @@ -5,7 +5,7 @@ from _algopy_testing import arc4 from _algopy_testing.primitives import BigUInt, String, UInt64 from _algopy_testing.primitives.array import FixedArray, ImmutableFixedArray -from algosdk import abi +from algokit_utils.applications import abi from tests.util import int_to_bytes diff --git a/tests/arc4/test_struct.py b/tests/arc4/test_struct.py index 6ae136bf..3353a34b 100644 --- a/tests/arc4/test_struct.py +++ b/tests/arc4/test_struct.py @@ -2,7 +2,7 @@ import pytest from _algopy_testing import arc4 -from algosdk import abi +from algokit_utils.applications import abi from tests.util import int_to_bytes diff --git a/tests/arc4/test_tuple.py b/tests/arc4/test_tuple.py index a4bb617c..410c2a76 100644 --- a/tests/arc4/test_tuple.py +++ b/tests/arc4/test_tuple.py @@ -2,8 +2,8 @@ import pytest from _algopy_testing import arc4 +from algokit_utils.applications import abi from algopy_testing import AlgopyTestContext, algopy_testing_context -from algosdk import abi from tests.artifacts.Tuples.contract import TuplesContract from tests.util import int_to_bytes diff --git a/tests/arc4/test_ufixednxm.py b/tests/arc4/test_ufixednxm.py index 2022228d..6d6cedf7 100644 --- a/tests/arc4/test_ufixednxm.py +++ b/tests/arc4/test_ufixednxm.py @@ -1,4 +1,5 @@ import typing +from decimal import Decimal import algokit_utils import pytest @@ -11,7 +12,7 @@ def _invalid_bytes_length_error(length: int) -> str: - return f"value string must be in bytes and correspond to a ufixed{length}" + return f"expected {length // 8} bytes for uint{length}" @pytest.mark.parametrize( @@ -71,7 +72,7 @@ def test_bigufixednxm_bytes(get_avm_result: AVMInvoker, value: str) -> None: def test_ufixednxm_from_bytes(get_avm_result: AVMInvoker, value: bytes) -> None: avm_result = get_avm_result("verify_ufixednxm_from_bytes", a=value) result = arc4.UFixedNxM[typing.Literal[32], typing.Literal[8]].from_bytes(value) - assert avm_result == int.from_bytes(result.bytes.value) + assert avm_result == int_to_decimal(int.from_bytes(result.bytes.value), 32, 8) @pytest.mark.parametrize( @@ -103,7 +104,8 @@ def test_ufixednxm_from_bytes_invalid_length(get_avm_result: AVMInvoker, value: def test_bigufixednxm_from_bytes(get_avm_result: AVMInvoker, value: bytes) -> None: avm_result = get_avm_result("verify_bigufixednxm_from_bytes", a=value) result = arc4.BigUFixedNxM[typing.Literal[256], typing.Literal[16]].from_bytes(value) - assert avm_result == int.from_bytes(result.bytes.value) + + assert avm_result == int_to_decimal(int.from_bytes(result.bytes.value), 256, 16) @pytest.mark.parametrize( @@ -137,8 +139,8 @@ def test_ufixednxm_from_log(get_avm_result: AVMInvoker, value: bytes, expected: result = arc4.UFixedNxM[typing.Literal[32], typing.Literal[8]].from_log( Bytes(ARC4_RETURN_PREFIX + value) ) - assert avm_result == expected - assert avm_result == int.from_bytes(result.bytes.value) + assert avm_result == int_to_decimal(expected, 32, 8) + assert avm_result == int_to_decimal(int.from_bytes(result.bytes.value), 32, 8) @pytest.mark.parametrize( @@ -192,8 +194,8 @@ def test_bigufixednxm_from_log(get_avm_result: AVMInvoker, value: bytes, expecte result = arc4.BigUFixedNxM[typing.Literal[256], typing.Literal[16]].from_log( Bytes(ARC4_RETURN_PREFIX + value) ) - assert avm_result == expected - assert avm_result == int.from_bytes(result.bytes.value) + assert avm_result == int_to_decimal(expected, 256, 16) + assert avm_result == int_to_decimal(int.from_bytes(result.bytes.value), 256, 16) @pytest.mark.parametrize( @@ -231,3 +233,9 @@ def test_bigufixednxm_from_log_invalid_length(get_avm_result: AVMInvoker, value: Bytes(ARC4_RETURN_PREFIX + value) ) assert result.bytes == value + + +def int_to_decimal(value: int, n: int, m: int) -> Decimal: + s = str(value) + d = n - m + return Decimal(f"{('0' * d + s[:-m])[-d:]}.{('0' * m + s[-m:])[-m:]}") diff --git a/tests/arc4/test_uintn.py b/tests/arc4/test_uintn.py index 537985a7..bbd8a936 100644 --- a/tests/arc4/test_uintn.py +++ b/tests/arc4/test_uintn.py @@ -12,7 +12,7 @@ def _invalid_bytes_length_error(length: int) -> str: - return f"value string must be in bytes and correspond to a uint{length}" + return f"expected {length // 8} bytes for uint{length}" @pytest.mark.parametrize( diff --git a/tests/common.py b/tests/common.py index 25c16656..3b7942c7 100644 --- a/tests/common.py +++ b/tests/common.py @@ -4,8 +4,8 @@ import typing from pathlib import Path -import algosdk from algokit_utils import ( + AddressWithSigners, AlgorandClient, AppClient, AppClientMethodCallParams, @@ -13,9 +13,9 @@ AppFactoryCreateMethodCallParams, AppFactoryCreateParams, AppFactoryParams, - SigningAccount, + AssetCreateParams, ) -from algosdk.v2client.algod import AlgodClient +from algokit_utils.transact import OnApplicationComplete INITIAL_BALANCE_MICRO_ALGOS = int(20e6) @@ -31,7 +31,7 @@ def __init__(self, client: AppClient, factory: AppFactory): def __call__( self, method: str, - on_complete: algosdk.transaction.OnComplete = algosdk.transaction.OnComplete.NoOpOC, + on_complete: OnApplicationComplete | None = None, *, return_raw: bool = False, **kwargs: typing.Any, @@ -53,13 +53,12 @@ def __call__( if return_raw and response.returns and len(response.returns) > 0: return response.returns[0].raw_value result = response.abi_return - if result is None and response.returns and len(response.returns) > 0: - assert response.returns[0].tx_info - return response.returns[0].tx_info.get("logs", None) + if result is None and response.confirmations and len(response.confirmations) > 0: + return response.confirmations[0].logs if isinstance(result, list) and all( isinstance(i, int) and i >= 0 and i <= 255 for i in result ): - return bytes(result) # type: ignore[arg-type] + return bytes(result) return result @@ -73,7 +72,7 @@ def create_avm_invoker(app_spec: Path, algorand: AlgorandClient) -> AVMInvoker: AppFactoryParams( algorand=algorand, app_spec=app_spec.read_text(), - default_sender=dispenser.address, + default_sender=dispenser.addr, ), ) try: @@ -90,8 +89,8 @@ def create_avm_invoker(app_spec: Path, algorand: AlgorandClient) -> AVMInvoker: def generate_test_asset( # noqa: PLR0913 *, - algod_client: AlgodClient, - sender: SigningAccount, + algorand: AlgorandClient, + sender: AddressWithSigners, total: int | None = None, decimals: int = 0, default_frozen: bool = False, @@ -116,33 +115,24 @@ def generate_test_asset( # noqa: PLR0913 f"${math.floor(random.random() * 100) + 1}_${total}" ) - params = algod_client.suggested_params() - - txn = algosdk.transaction.AssetConfigTxn( - sender=sender.address, - sp=params, - total=total * 10**decimals, - decimals=decimals, - default_frozen=default_frozen, - unit_name=unit_name, - asset_name=asset_name, - manager=manager, - reserve=reserve, - freeze=freeze, - clawback=clawback, - url=url, - metadata_hash=metadata_hash, - note=note or _random_note(), - lease=lease, - strict_empty_address_check=False, - rekey_to=rekey_to, - ) # type: ignore[no-untyped-call, unused-ignore] - - signed_transaction = txn.sign(sender.private_key) # type: ignore[no-untyped-call, unused-ignore] - algod_client.send_transaction(signed_transaction) - ptx = algod_client.pending_transaction_info(txn.get_txid()) # type: ignore[no-untyped-call, unused-ignore] - - if isinstance(ptx, dict) and "asset-index" in ptx and isinstance(ptx["asset-index"], int): - return ptx["asset-index"] - else: - raise ValueError("Unexpected response from pending_transaction_info") + result = algorand.send.asset_create( + AssetCreateParams( + sender=sender.addr, + total=total * 10**decimals, + decimals=decimals, + default_frozen=default_frozen, + unit_name=unit_name, + asset_name=asset_name, + manager=manager, + reserve=reserve, + freeze=freeze, + clawback=clawback, + url=url, + metadata_hash=metadata_hash, + note=note or _random_note(), + lease=lease, + rekey_to=rekey_to, + signer=sender.signer, + ) + ) + return result.asset_id diff --git a/tests/conftest.py b/tests/conftest.py index 1994f09e..933333dd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ def algorand() -> AlgorandClient: @pytest.fixture() def localnet_creator_address(algorand: AlgorandClient) -> str: - return algorand.account.localnet_dispenser().address + return algorand.account.localnet_dispenser().addr @pytest.fixture() diff --git a/tests/test_array.py b/tests/test_array.py index cd890b6d..cec24af9 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -1,8 +1,8 @@ import typing -import algosdk import pytest from _algopy_testing.constants import MAX_UINT512 +from algokit_utils.applications import abi from algopy import ImmutableArray, String, UInt64, arc4 from algopy_testing import AlgopyTestContext, algopy_testing_context @@ -275,4 +275,4 @@ def test_nested_immutable(context: AlgopyTestContext) -> None: # noqa: ARG001 def _get_arc4_bytes(arc4_type: str, value: object) -> bytes: - return algosdk.abi.ABIType.from_string(arc4_type).encode(value) + return abi.ABIType.from_string(arc4_type).encode(value) diff --git a/tests/test_context.py b/tests/test_context.py index 18d6bf38..42f6cdad 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -2,7 +2,6 @@ from contextlib import ExitStack import algopy.itxn -import algosdk import pytest from _algopy_testing import algopy_testing_context, arc4 from _algopy_testing.constants import MAX_UINT8, MAX_UINT16, MAX_UINT32, MAX_UINT64, MAX_UINT512 @@ -10,6 +9,7 @@ from _algopy_testing.context_helpers import lazy_context from _algopy_testing.context_helpers.txn_context import TransactionGroup from _algopy_testing.itxn import PaymentInnerTransaction +from _algopy_testing.utils import generate_random_account from algopy import Bytes, TransactionType, UInt64 from tests.artifacts.Arc4InnerTxns.contract import Arc4InnerTxnsContract @@ -36,7 +36,7 @@ def test_patch_global_fields() -> None: def test_account_management() -> None: with algopy_testing_context() as context: - address: str = algosdk.account.generate_account()[1] + address: str = generate_random_account().addr account = context.any.account(address=address, balance=UInt64(1000)) assert context.ledger.get_account(account.public_key).balance == 1000 diff --git a/tests/test_op.py b/tests/test_op.py index b70cb52c..b423a9c0 100644 --- a/tests/test_op.py +++ b/tests/test_op.py @@ -5,18 +5,20 @@ import _algopy_testing import algopy -import algosdk import coincurve import ecdsa # type: ignore # noqa: PGH003 import ecdsa.util # type: ignore # noqa: PGH003 import nacl.signing import pytest +from _algopy_testing.utils import generate_random_account from algokit_utils import ( AlgoAmount, AlgorandClient, AppClientMethodCallParams, LogicError, + LogicSigAccount, ) +from algokit_utils.common import MIN_TXN_FEE, ZERO_ADDRESS, public_key_from_address from algopy import op from algopy_testing import AlgopyTestContext, algopy_testing_context, arc4_prefix from Cryptodome.Hash import keccak @@ -240,9 +242,9 @@ def test_ed25519verify( message = b"Test message for ed25519 verification" # Generate key pair and sign the message - private_key, public_key = algosdk.account.generate_account() - public_key = algosdk.encoding.decode_address(public_key) - signature = algosdk.logic.teal_sign_from_program(private_key, message, approval) + acc = generate_random_account() + public_key = public_key_from_address(acc.addr) + signature = acc.program_data_signer(LogicSigAccount(logic=approval), message) # Verify the signature using AVM and local op avm_result = get_crypto_ops_avm_result( @@ -331,7 +333,7 @@ def test_verify_ecdsa_recover_k1( d=d, static_fee=AlgoAmount(micro_algo=5000), ) - assert isinstance(result, list) + assert isinstance(result, tuple) result_x, result_y = bytes(result[0]), bytes(result[1]) assert result_x == expected_x, "X coordinate mismatch" @@ -355,7 +357,7 @@ def test_verify_ecdsa_decompress_k1( a=pk.format(compressed=True), static_fee=AlgoAmount(micro_algo=3000), ) - assert isinstance(result, list) + assert isinstance(result, tuple) result_x, result_y = bytes(result[0]), bytes(result[1]) assert result_x == pk.point()[0].to_bytes(32, byteorder="big"), "X coordinate mismatch" @@ -399,7 +401,7 @@ def test_asset_holding_get( dummy_account_a = algorand.account.localnet_dispenser() expected_balance = 100 dummy_asset = generate_test_asset( - algod_client=algorand.client.algod, + algorand=algorand, total=expected_balance, sender=dummy_account_a, decimals=0, @@ -408,13 +410,13 @@ def test_asset_holding_get( avm_asset_balance = get_state_asset_holding_avm_result( "verify_asset_holding_get", - a=dummy_account_a.address, + a=dummy_account_a.addr, b=dummy_asset, static_fee=AlgoAmount(micro_algo=1000), ) avm_frozen_balance = get_state_asset_holding_avm_result( "verify_asset_frozen_get", - a=dummy_account_a.address, + a=dummy_account_a.addr, b=dummy_asset, static_fee=AlgoAmount(micro_algo=1000), ) @@ -440,10 +442,10 @@ def test_asset_holding_get( ("verify_asset_params_get_name", b"TEST"), ("verify_asset_params_get_url", b"https://algorand.co"), ("verify_asset_params_get_metadata_hash", b"test" + b" " * 28), - ("verify_asset_params_get_manager", algosdk.constants.ZERO_ADDRESS), - ("verify_asset_params_get_reserve", algosdk.constants.ZERO_ADDRESS), - ("verify_asset_params_get_freeze", algosdk.constants.ZERO_ADDRESS), - ("verify_asset_params_get_clawback", algosdk.constants.ZERO_ADDRESS), + ("verify_asset_params_get_manager", ZERO_ADDRESS), + ("verify_asset_params_get_reserve", ZERO_ADDRESS), + ("verify_asset_params_get_freeze", ZERO_ADDRESS), + ("verify_asset_params_get_clawback", ZERO_ADDRESS), ("verify_asset_params_get_creator", "creator"), ], ) @@ -455,7 +457,7 @@ def test_asset_params_get( expected_value: object, ) -> None: dummy_account = algorand.account.localnet_dispenser() - creator = dummy_account.address + creator = dummy_account.addr metadata_hash = b"test" + b" " * 28 mock_asset = context.any.asset( @@ -469,7 +471,7 @@ def test_asset_params_get( ) dummy_asset = generate_test_asset( - algod_client=algorand.client.algod, + algorand=algorand, total=100, sender=dummy_account, decimals=0, @@ -528,7 +530,7 @@ def test_app_params_get( local_num_uint=algopy.UInt64(0), local_num_bytes=algopy.UInt64(0), extra_program_pages=algopy.UInt64(0), - creator=algopy.Account(algorand.account.localnet_dispenser().address), + creator=algopy.Account(algorand.account.localnet_dispenser().addr), ) contract = StateAppParamsContract() @@ -553,7 +555,7 @@ def test_app_params_get( [ ("verify_acct_balance", INITIAL_BALANCE_MICRO_ALGOS + 100_000), ("verify_acct_min_balance", 100_000), - ("verify_acct_auth_addr", algosdk.constants.ZERO_ADDRESS), + ("verify_acct_auth_addr", ZERO_ADDRESS), ("verify_acct_total_num_uint", 0), ("verify_acct_total_num_byte_slice", 0), ("verify_acct_total_extra_app_pages", 0), @@ -582,10 +584,10 @@ def test_acct_params_get( ) mock_account = context.any.account( - address=dummy_account.address, + address=dummy_account.addr, balance=algopy.UInt64(INITIAL_BALANCE_MICRO_ALGOS + 100_000), min_balance=algopy.UInt64(100_000), - auth_address=algopy.Account(algosdk.constants.ZERO_ADDRESS), + auth_address=algopy.Account(ZERO_ADDRESS), total_num_uint=algopy.UInt64(0), total_num_byte_slice=algopy.UInt64(0), total_extra_app_pages=algopy.UInt64(0), @@ -600,7 +602,7 @@ def test_acct_params_get( mock_contract = StateAcctParamsGetContract() avm_result = get_state_acct_params_avm_result( - method_name, a=dummy_account.address, static_fee=AlgoAmount(micro_algo=1000) + method_name, a=dummy_account.addr, static_fee=AlgoAmount(micro_algo=1000) ) with context.txn.create_group( active_txn_overrides={"fee": algopy.UInt64(1000), "sender": mock_account} @@ -681,7 +683,7 @@ def test_app_local_ex_get( assert mock_secondary_app.local_num_uint == 1 assert mock_secondary_app.local_num_bytes == 2 - with contextlib.suppress(algosdk.error.AlgodHTTPError): + with contextlib.suppress(ValueError): get_state_app_local_ex_avm_result.client.send.opt_in( AppClientMethodCallParams(method="opt_in", note=secrets.token_bytes(8)) ) @@ -715,7 +717,7 @@ def test_app_local_ex_get_arc4( assert mock_secondary_app.local_num_uint == 1 assert mock_secondary_app.local_num_bytes == 2 - with contextlib.suppress(algosdk.error.AlgodHTTPError, Exception): + with contextlib.suppress(ValueError): get_state_app_local_ex_avm_result.client.send.opt_in( AppClientMethodCallParams(method="opt_in", note=secrets.token_bytes(8)) ) @@ -818,7 +820,7 @@ def test_app_global_ex_get( mock_result = contract.verify_get_ex_bytes( a=mock_secondary_app, b=algopy.Bytes(b"global_bytes_explicit") ) - assert avm_result[0] == list(mock_result[0].value) # type: ignore[index] + assert avm_result[0] == mock_result[0].value # type: ignore[index] assert avm_result[1] == mock_result[1] # type: ignore[index] @@ -849,7 +851,7 @@ def test_app_global_ex_get_arc4( mock_result = contract.verify_get_ex_bytes( a=mock_secondary_app, b=algopy.Bytes(b"global_arc4_bytes_explicit") ) - assert avm_result[0] == list(mock_result[0].value) # type: ignore[index] + assert avm_result[0] == mock_result[0].value # type: ignore[index] assert avm_result[1] == mock_result[1] # type: ignore[index] @@ -904,7 +906,7 @@ def test_itxn_ops(context: AlgopyTestContext) -> None: ] assert approval_pages == [appl_itxn.approval_program] assert appl_itxn.on_completion == algopy.OnCompleteAction.DeleteApplication - assert appl_itxn.fee == algopy.UInt64(algosdk.constants.MIN_TXN_FEE) + assert appl_itxn.fee == algopy.UInt64(MIN_TXN_FEE) assert appl_itxn.sender == context.ledger.get_app(contract).address # NOTE: would implementing emulation for this behavior be useful # in unit testing context (vs integration tests)? diff --git a/tests/utilities/test_log.py b/tests/utilities/test_log.py index b08c2c21..e285b500 100644 --- a/tests/utilities/test_log.py +++ b/tests/utilities/test_log.py @@ -1,4 +1,3 @@ -import base64 import typing from collections.abc import Generator @@ -35,7 +34,7 @@ def test_log(get_avm_result: AVMInvoker, context: AlgopyTestContext) -> None: n = arc4.Tuple((arc4.UInt32(1), arc4.UInt64(2), arc4.String("hello"))) o = FixedBytes[typing.Literal[5]](b"hello") - avm_result_ = get_avm_result( + avm_result = get_avm_result( "verify_log", a=a.value, b=b.value, @@ -52,8 +51,7 @@ def test_log(get_avm_result: AVMInvoker, context: AlgopyTestContext) -> None: n=n.bytes.value, o=o.bytes.value, ) - assert isinstance(avm_result_, list) - avm_result = [base64.b64decode(b) for b in avm_result_] + assert isinstance(avm_result, list) with context.txn.create_group([context.any.txn.payment()]): # noqa: SIM117 with pytest.raises(RuntimeError, match="Can only add logs to ApplicationCallTransaction!"): diff --git a/tests/value_generators/test_avm.py b/tests/value_generators/test_avm.py index fabd3e47..32991edd 100644 --- a/tests/value_generators/test_avm.py +++ b/tests/value_generators/test_avm.py @@ -2,7 +2,6 @@ from collections.abc import Iterator import algopy -import algosdk import pytest from _algopy_testing import algopy_testing_context from _algopy_testing.constants import MAX_BYTES_SIZE, MAX_UINT64 @@ -10,7 +9,7 @@ from _algopy_testing.primitives.bytes import Bytes from _algopy_testing.primitives.fixed_bytes import FixedBytes from _algopy_testing.primitives.string import String -from _algopy_testing.utils import get_type_generic_from_int_literal +from _algopy_testing.utils import generate_random_account, get_type_generic_from_int_literal @pytest.fixture() @@ -87,7 +86,7 @@ def test_avm_account_generator(context: AlgopyTestContext) -> None: assert context.ledger.account_is_funded(account.public_key) # unfunded - custom_address = algosdk.account.generate_account()[1] + custom_address = generate_random_account().addr account = context.any.account(address=custom_address) assert isinstance(account, algopy.Account) assert account.public_key == custom_address