Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
"python.analysis.typeCheckingMode": "off",

"ruff.enable": true,
"ruff.lint.run": "onSave",
"ruff.lint.args": ["--config=pyproject.toml"],
"ruff.configuration": "pyproject.toml",
"ruff.importStrategy": "fromEnvironment",
"ruff.fixAll": true, //lint and fix all files in workspace
"ruff.organizeImports": true, //organize imports on save
Expand Down
45 changes: 35 additions & 10 deletions src/_algopy_testing/arc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)

if typing.TYPE_CHECKING:
from collections.abc import Iterable, Iterator, Sequence
from collections.abc import Callable, Iterable, Iterator, Sequence

import algopy

Expand All @@ -56,24 +56,27 @@
"Struct",
"Tuple",
"UFixedNxM",
"UInt128",
"UInt8",
"UInt16",
"UInt256",
"UInt32",
"UInt512",
"UInt64",
"UInt8",
"UInt128",
"UInt256",
"UInt512",
"UIntN",
"abi_call",
"arc4_create",
"arc4_update",
"arc4_signature",
"arc4_update",
"emit",
]

_ABI_LENGTH_SIZE = 2
_TBitSize = typing.TypeVar("_TBitSize", bound=int)

_P = typing.ParamSpec("_P")
_R = typing.TypeVar("_R")


class _TypeInfo:
@property
Expand Down Expand Up @@ -179,12 +182,22 @@ def __hash__(self) -> int:
return hash(self.bytes)


def arc4_signature(signature: str, /) -> algopy.Bytes:
def arc4_signature(signature: str | Callable[_P, _R], /) -> algopy.Bytes:
"""Convert a signature to ARC4 bytes."""
import algopy

from _algopy_testing.decorators.arc4 import get_arc4_metadata

if isinstance(signature, str):
method_signature = signature
else:
arc4_signature = get_arc4_metadata(signature).arc4_signature
if arc4_signature is None:
raise ValueError("signature not found")
method_signature = arc4_signature

hashed_signature = SHA512.new(truncate="256")
hashed_signature.update(signature.encode("utf-8"))
hashed_signature.update(method_signature.encode("utf-8"))
return_value = hashed_signature.digest()[:4]
return algopy.Bytes(return_value)

Expand Down Expand Up @@ -1187,11 +1200,17 @@ def _find_bool(
) -> int:
"""Helper function to find consecutive booleans from current index in a tuple."""
until = 0
is_looking_forward = delta > 0
is_looking_backward = delta < 0
values_length = len(values) if isinstance(values, tuple | list) else values.length.value
while True:
curr = index + delta * until
is_curr_at_end = curr == values_length - 1
is_curr_at_start = curr == 0
if isinstance(values[curr], Bool):
if curr != values_length - 1 and delta > 0 or curr > 0 and delta < 0:
if (is_looking_forward and not is_curr_at_end) or (
is_looking_backward and not is_curr_at_start
):
until += 1
else:
break
Expand All @@ -1204,11 +1223,17 @@ def _find_bool(
def _find_bool_types(values: typing.Sequence[_TypeInfo], index: int, delta: int) -> int:
"""Helper function to find consecutive booleans from current index in a tuple."""
until = 0
is_looking_forward = delta > 0
is_looking_backward = delta < 0
values_length = len(values)
while True:
curr = index + delta * until
is_curr_at_end = curr == values_length - 1
is_curr_at_start = curr == 0
if isinstance(values[curr], _BoolTypeInfo):
if curr != values_length - 1 and delta > 0 or curr > 0 and delta < 0:
if (is_looking_forward and not is_curr_at_end) or (
is_looking_backward and not is_curr_at_start
):
until += 1
else:
break
Expand Down
8 changes: 8 additions & 0 deletions tests/arc4/test_arc4_method_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def test_app_args_is_correct_with_simple_args(
b"\x00\x05hello",
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.sink)


def test_app_args_is_correct_with_alias(
Expand All @@ -106,6 +107,7 @@ def test_app_args_is_correct_with_alias(
b"\x00\x05hello",
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.sink2)


def test_app_args_is_correct_with_txn(
Expand Down Expand Up @@ -148,6 +150,7 @@ def test_app_args_is_correct_with_txn(
b"\x00\x05hello",
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.with_txn)


def test_app_args_is_correct_with_asset(
Expand Down Expand Up @@ -186,6 +189,7 @@ def test_app_args_is_correct_with_asset(
b"\x00",
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.with_asset)


def test_app_args_is_correct_with_account(
Expand Down Expand Up @@ -219,6 +223,7 @@ def test_app_args_is_correct_with_account(
b"\x01",
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.with_acc)


def test_app_args_is_correct_with_application(
Expand Down Expand Up @@ -260,6 +265,7 @@ def test_app_args_is_correct_with_application(
other_app_id.to_bytes(length=8), # app id as bytes
b"\x00\x02\x01\x02",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.with_app)
assert app_foreign_apps == [
self_app.id,
other_app_id,
Expand Down Expand Up @@ -298,6 +304,7 @@ def test_app_args_is_correct_with_complex(
b"\x01", # 0th index is the sender
b"\x00\x01\x05",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.complex_sig)
assert result[0].bytes == struct.another_struct.bytes
assert result[1].bytes == struct.bytes

Expand Down Expand Up @@ -359,5 +366,6 @@ def test_prepare_txns_with_complex(
b"\x01", # 0th index is the sender
b"\x00\x01\x05",
]
assert app_args[0] == arc4.arc4_signature(SignaturesContract.complex_sig)
assert result[0].bytes == struct.another_struct.bytes
assert result[1].bytes == struct.bytes