Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 2 additions & 3 deletions examples/htlc_logicsig/test_signature.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections.abc import Generator

import algopy
import algosdk
import pytest
from algopy_testing import AlgopyTestContext, algopy_testing_context

Expand All @@ -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"
),
Expand Down
5 changes: 2 additions & 3 deletions examples/proof_of_attendance/test_contract.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections.abc import Generator

import algopy
import algosdk
import pytest
from algopy_testing import AlgopyTestContext, algopy_testing_context

Expand Down Expand Up @@ -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),
Expand Down
25 changes: 5 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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",
Expand Down Expand Up @@ -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",
]

Expand All @@ -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
Expand All @@ -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",
]
Expand Down Expand Up @@ -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
]
Expand Down Expand Up @@ -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"

Expand Down
10 changes: 5 additions & 5 deletions src/_algopy_testing/arc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand All @@ -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]
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/_algopy_testing/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@
b"\x09" # pragma version 9
b"\x81\x01" # pushint 1
)

LOGIC_DATA_PREFIX = b"ProgData"
7 changes: 2 additions & 5 deletions src/_algopy_testing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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] = ()
Expand Down
6 changes: 3 additions & 3 deletions src/_algopy_testing/context_helpers/ledger_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions src/_algopy_testing/context_helpers/txn_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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."
)

Expand Down
21 changes: 7 additions & 14 deletions src/_algopy_testing/decorators/arc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions src/_algopy_testing/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import typing

from algosdk import constants
from algokit_utils.transact import TransactionType as BaseTransactionType

from _algopy_testing.primitives import UInt64

Expand Down Expand Up @@ -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")

Expand Down
4 changes: 2 additions & 2 deletions src/_algopy_testing/itxn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 5 additions & 7 deletions src/_algopy_testing/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
4 changes: 2 additions & 2 deletions src/_algopy_testing/models/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading