diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ed4de8b..b026ae5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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 diff --git a/src/_algopy_testing/arc4.py b/src/_algopy_testing/arc4.py index 3f2ee95a..e60ebe07 100644 --- a/src/_algopy_testing/arc4.py +++ b/src/_algopy_testing/arc4.py @@ -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 @@ -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 @@ -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) @@ -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 @@ -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 diff --git a/tests/arc4/test_arc4_method_signature.py b/tests/arc4/test_arc4_method_signature.py index 5355fbd1..8b1f33c7 100644 --- a/tests/arc4/test_arc4_method_signature.py +++ b/tests/arc4/test_arc4_method_signature.py @@ -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( @@ -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( @@ -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( @@ -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( @@ -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( @@ -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, @@ -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 @@ -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