diff --git a/examples/amm/out/ConstantProductAMM.arc56.json b/examples/amm/out/ConstantProductAMM.arc56.json index 3182171237..4638da7049 100644 --- a/examples/amm/out/ConstantProductAMM.arc56.json +++ b/examples/amm/out/ConstantProductAMM.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "ConstantProductAMM", "structs": {}, "methods": [ @@ -24,9 +20,9 @@ ] }, "readonly": false, + "desc": "sets the governor of the contract, may only be called by the current governor", "events": [], - "recommendations": {}, - "desc": "sets the governor of the contract, may only be called by the current governor" + "recommendations": {} }, { "name": "bootstrap", @@ -58,9 +54,9 @@ ] }, "readonly": false, + "desc": "bootstraps the contract by opting into the assets and creating the pool token.\nNote this method will fail if it is attempted more than once on the same contract since the assets and pool token application state values are marked as static and cannot be overridden.", "events": [], - "recommendations": {}, - "desc": "bootstraps the contract by opting into the assets and creating the pool token.\nNote this method will fail if it is attempted more than once on the same contract since the assets and pool token application state values are marked as static and cannot be overridden." + "recommendations": {} }, { "name": "mint", @@ -116,9 +112,9 @@ ] }, "readonly": false, + "desc": "mint pool tokens given some amount of asset A and asset B.\nGiven some amount of Asset A and Asset B in the transfers, mint some number of pool tokens commensurate with the pools current balance and circulating supply of pool tokens.", "events": [], - "recommendations": {}, - "desc": "mint pool tokens given some amount of asset A and asset B.\nGiven some amount of Asset A and Asset B in the transfers, mint some number of pool tokens commensurate with the pools current balance and circulating supply of pool tokens." + "recommendations": {} }, { "name": "burn", @@ -169,9 +165,9 @@ ] }, "readonly": false, + "desc": "burn pool tokens to get back some amount of asset A and asset B", "events": [], - "recommendations": {}, - "desc": "burn pool tokens to get back some amount of asset A and asset B" + "recommendations": {} }, { "name": "swap", @@ -212,11 +208,16 @@ ] }, "readonly": false, + "desc": "Swap some amount of either asset A or asset B for the other", "events": [], - "recommendations": {}, - "desc": "Swap some amount of either asset A or asset B for the other" + "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -271,9 +272,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -502,5 +500,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/amm/out/client_ConstantProductAMM.py b/examples/amm/out/client_ConstantProductAMM.py index 27dddb37a5..1ce44d23e3 100644 --- a/examples/amm/out/client_ConstantProductAMM.py +++ b/examples/amm/out/client_ConstantProductAMM.py @@ -21,7 +21,7 @@ def bootstrap( b_asset: algopy.Asset, ) -> algopy.arc4.UIntN[typing.Literal[64]]: ... - @algopy.arc4.abimethod(default_args={'pool_asset': 'pool_token', 'a_asset': 'asset_a', 'b_asset': 'asset_b'}) + @algopy.arc4.abimethod def mint( self, a_xfer: algopy.gtxn.AssetTransferTransaction, @@ -31,7 +31,7 @@ def mint( b_asset: algopy.Asset, ) -> None: ... - @algopy.arc4.abimethod(default_args={'pool_asset': 'pool_token', 'a_asset': 'asset_a', 'b_asset': 'asset_b'}) + @algopy.arc4.abimethod def burn( self, pool_xfer: algopy.gtxn.AssetTransferTransaction, @@ -40,7 +40,7 @@ def burn( b_asset: algopy.Asset, ) -> None: ... - @algopy.arc4.abimethod(default_args={'a_asset': 'asset_a', 'b_asset': 'asset_b'}) + @algopy.arc4.abimethod def swap( self, swap_xfer: algopy.gtxn.AssetTransferTransaction, diff --git a/examples/arc_28/out/EventEmitter.arc56.json b/examples/arc_28/out/EventEmitter.arc56.json index 2083608282..19d5c6d073 100644 --- a/examples/arc_28/out/EventEmitter.arc56.json +++ b/examples/arc_28/out/EventEmitter.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "EventEmitter", "structs": {}, "methods": [ @@ -98,6 +94,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -126,49 +127,6 @@ ], "call": [] }, - "events": [ - { - "name": "Swapped", - "args": [ - { - "type": "uint64", - "name": "a" - }, - { - "type": "uint64", - "name": "b" - } - ] - }, - { - "name": "Swapped", - "args": [ - { - "type": "uint64", - "name": "field1" - }, - { - "type": "uint64", - "name": "field2" - } - ] - }, - { - "name": "AnEvent", - "args": [ - { - "type": "ufixed256x16", - "name": "field1" - }, - { - "type": "ufixed64x2", - "name": "field2" - } - ] - } - ], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -215,5 +173,47 @@ "minor": 99, "patch": 99 } - } + }, + "events": [ + { + "name": "Swapped", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ] + }, + { + "name": "Swapped", + "args": [ + { + "type": "uint64", + "name": "field1" + }, + { + "type": "uint64", + "name": "field2" + } + ] + }, + { + "name": "AnEvent", + "args": [ + { + "type": "ufixed256x16", + "name": "field1" + }, + { + "type": "ufixed64x2", + "name": "field2" + } + ] + } + ], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/auction/out/Auction.arc56.json b/examples/auction/out/Auction.arc56.json index c69aab435f..41a1ff759d 100644 --- a/examples/auction/out/Auction.arc56.json +++ b/examples/auction/out/Auction.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Auction", "structs": {}, "methods": [ @@ -131,6 +127,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -192,9 +193,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -356,5 +354,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/box_storage/out/BoxContract.arc56.json b/examples/box_storage/out/BoxContract.arc56.json index 6dab0a72b7..d2ed0d114f 100644 --- a/examples/box_storage/out/BoxContract.arc56.json +++ b/examples/box_storage/out/BoxContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "BoxContract", "structs": {}, "methods": [ @@ -252,6 +248,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -312,9 +313,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -471,5 +469,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/hello_world_arc4/out/HelloWorldContract.arc56.json b/examples/hello_world_arc4/out/HelloWorldContract.arc56.json index ca6fe789d2..d4b3c07a88 100644 --- a/examples/hello_world_arc4/out/HelloWorldContract.arc56.json +++ b/examples/hello_world_arc4/out/HelloWorldContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloWorldContract", "structs": {}, "methods": [ @@ -28,6 +24,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -56,9 +57,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -103,5 +101,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/merkle/out/MerkleTree.arc56.json b/examples/merkle/out/MerkleTree.arc56.json index 87224fb87c..ac98e02f4c 100644 --- a/examples/merkle/out/MerkleTree.arc56.json +++ b/examples/merkle/out/MerkleTree.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "MerkleTree", "structs": {}, "methods": [ @@ -53,6 +49,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -85,9 +86,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -139,5 +137,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/sizes.txt b/examples/sizes.txt index ed96652bee..d7c80e8a38 100644 --- a/examples/sizes.txt +++ b/examples/sizes.txt @@ -21,6 +21,7 @@ arc4_types/Arc4StructsType 302 239 - | 204 121 - arc4_types/Arc4TuplesType 795 136 - | 537 58 - arc_28/EventEmitter 186 133 - | 100 64 - + arc_56 678 465 - | 355 190 - asset/Reference 268 261 - | 144 141 - auction/Auction 592 522 - | 328 281 - augmented_assignment/Augmented 151 156 - | 77 78 - @@ -130,4 +131,4 @@ unssa/UnSSA 432 368 - | 241 204 - voting/VotingRoundApp 1593 1483 - | 734 649 - with_reentrancy/WithReentrancy 255 242 - | 132 122 - - Total 69194 53532 53473 | 32840 21757 21713 \ No newline at end of file + Total 69872 53997 53938 | 33195 21947 21903 \ No newline at end of file diff --git a/examples/struct_in_box/out/ExampleContract.arc56.json b/examples/struct_in_box/out/ExampleContract.arc56.json index 812768da15..977e89f1a5 100644 --- a/examples/struct_in_box/out/ExampleContract.arc56.json +++ b/examples/struct_in_box/out/ExampleContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "ExampleContract", "structs": { "UserStruct": [ @@ -91,6 +87,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -119,9 +120,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -182,5 +180,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/struct_in_box/out/client_ExampleContract.py b/examples/struct_in_box/out/client_ExampleContract.py index 3033b5dd8e..fb5e2d2137 100644 --- a/examples/struct_in_box/out/client_ExampleContract.py +++ b/examples/struct_in_box/out/client_ExampleContract.py @@ -28,4 +28,4 @@ def attach_asset_to_user( def get_user( self, user_id: algopy.arc4.UIntN[typing.Literal[64]], - ) -> algopy.arc4.Tuple[algopy.arc4.String, algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.UIntN[typing.Literal[64]]]: ... + ) -> UserStruct: ... diff --git a/examples/tictactoe/out/TicTacToeContract.arc56.json b/examples/tictactoe/out/TicTacToeContract.arc56.json index 7180e292a2..671d7b38dd 100644 --- a/examples/tictactoe/out/TicTacToeContract.arc56.json +++ b/examples/tictactoe/out/TicTacToeContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "TicTacToeContract", "structs": {}, "methods": [ @@ -88,6 +84,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -140,9 +141,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -264,5 +262,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/voting/out/VotingRoundApp.arc56.json b/examples/voting/out/VotingRoundApp.arc56.json index 78d6337250..62932282bc 100644 --- a/examples/voting/out/VotingRoundApp.arc56.json +++ b/examples/voting/out/VotingRoundApp.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "VotingRoundApp", "structs": { "VotingPreconditions": [ @@ -163,6 +159,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -267,9 +268,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -491,5 +489,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/examples/voting/out/client_VotingRoundApp.py b/examples/voting/out/client_VotingRoundApp.py index d41a3400bc..394fb0fe69 100644 --- a/examples/voting/out/client_VotingRoundApp.py +++ b/examples/voting/out/client_VotingRoundApp.py @@ -40,7 +40,7 @@ def close( def get_preconditions( self, signature: algopy.arc4.DynamicBytes, - ) -> algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.UIntN[typing.Literal[64]]]: ... + ) -> VotingPreconditions: ... @algopy.arc4.abimethod def vote( diff --git a/src/puya/arc56.py b/src/puya/arc56.py index b1d6e6ad24..899779dd41 100644 --- a/src/puya/arc56.py +++ b/src/puya/arc56.py @@ -65,7 +65,7 @@ def create_arc56_json( }, methods=[ models.Method( - name=m.name, + name=m.config.name, desc=m.desc, args=[ models.MethodArg( @@ -244,12 +244,12 @@ def _method_actions(method: ARC4BareMethod | ARC4ABIMethod) -> models.MethodActi create=[ oca.name for oca in config.allowed_completion_types - if config.create != ARC4CreateOption.disallow and _allowed_create_oca(oca.name) + if config.create != ARC4CreateOption.disallow and allowed_create_oca(oca.name) ], call=[ oca.name for oca in config.allowed_completion_types - if config.create != ARC4CreateOption.require and _allowed_call_oca(oca.name) + if config.create != ARC4CreateOption.require and allowed_call_oca(oca.name) ], ) @@ -294,13 +294,13 @@ def _combine_actions(actions: Sequence[models.MethodActions]) -> models.MethodAc ) -def _allowed_create_oca( +def allowed_create_oca( oca: str, ) -> typing.TypeGuard[typing.Literal["NoOp", "OptIn", "DeleteApplication"]]: return oca in ("NoOp", "OptIn", "DeleteApplication") -def _allowed_call_oca( +def allowed_call_oca( oca: str, ) -> typing.TypeGuard[ typing.Literal["NoOp", "OptIn", "CloseOut", "UpdateApplication", "DeleteApplication"] diff --git a/src/puya/arc56_models.py b/src/puya/arc56_models.py index cae64686c3..c5eb67231d 100644 --- a/src/puya/arc56_models.py +++ b/src/puya/arc56_models.py @@ -232,9 +232,9 @@ class Method: """Allowed actions for this method""" readonly: bool """If this method does not write anything to the ledger (ARC-22)""" - events: Sequence[Event] + events: Sequence[Event] = () """ARC-28 events that MAY be emitted by this method""" - recommendations: MethodRecommendations + recommendations: MethodRecommendations | None = None """Information that clients can use when calling the method""" @@ -309,7 +309,7 @@ class Contract: This interface is an extension of the interface described in ARC-4 """ - arcs: Sequence[int] + arcs: Sequence[int] = () """ The ARCs used and/or supported by this contract. All contracts implicitly support ARC-4 and ARC-56 @@ -333,8 +333,8 @@ class Contract: """ methods: Sequence[Method] """All of the methods that the contract implements""" - state: ContractState - bareActions: MethodActions + state: ContractState | None = None + bareActions: MethodActions | None = None """Supported bare actions for the contract""" sourceInfo: Mapping[ProgramType, ProgramSourceInfo] | None = None """Information about the TEAL programs""" @@ -353,9 +353,9 @@ class Contract: Information used to get the given byteCode and/or PC values in sourceInfo. MUST be given if byteCode or PC values are present """ - events: Sequence[Event] + events: Sequence[Event] | None = None """ARC-28 events that MAY be emitted by this contract""" - templateVariables: Mapping[str, TemplateVariable] + templateVariables: Mapping[str, TemplateVariable] | None = None """ A mapping of template variable names as they appear in the teal (not including TMPL_ prefix) to their respective types and values (if applicable) diff --git a/src/puyapy/awst_build/arc32_client_gen.py b/src/puyapy/awst_build/arc32_client_gen.py deleted file mode 100644 index dafc45bf97..0000000000 --- a/src/puyapy/awst_build/arc32_client_gen.py +++ /dev/null @@ -1,138 +0,0 @@ -import itertools -import textwrap -import typing -from collections.abc import Iterable, Mapping, Sequence -from pathlib import Path - -from puya import log -from puya.models import ( - ARC4ABIMethod, - ARC4CreateOption, - ARC4Method, - ARC4MethodArg, - ARC4Struct, - OnCompletionAction, -) -from puya.utils import make_path_relative_to_cwd, unique - -from puyapy.awst_build import constants -from puyapy.awst_build.arc4_utils import arc4_to_pytype - -logger = log.get_logger(__name__) - -_AUTO_GENERATED_COMMENT = "# This file is auto-generated, do not modify" -_INDENT = " " * 4 - - -def write_arc32_client( - name: str, structs: Mapping[str, ARC4Struct], methods: Sequence[ARC4Method], out_dir: Path -) -> None: - stub_path = out_dir / f"client_{name}.py" - if _can_overwrite_auto_generated_file(stub_path): - logger.info(f"writing {make_path_relative_to_cwd(stub_path)}") - stub_text = _create_arc32_stub(name, structs, methods) - stub_path.write_text(stub_text) - else: - logger.error( - f"Not outputting {make_path_relative_to_cwd(stub_path)} " - "since content does not appear to be auto-generated" - ) - - -def _can_overwrite_auto_generated_file(path: Path) -> bool: - return not path.exists() or path.read_text().startswith(_AUTO_GENERATED_COMMENT) - - -def _create_arc32_stub( - name: str, structs: Mapping[str, ARC4Struct], methods: Sequence[ARC4Method] -) -> str: - abi_methods = [m for m in methods if isinstance(m, ARC4ABIMethod)] - return "\n".join( - ( - _AUTO_GENERATED_COMMENT, - "# flake8: noqa", # this works for flake8 and ruff - "# fmt: off", # disable formatting" - "import typing", - "", - "import algopy", - "", - *itertools.chain(*(_abi_struct_to_class(s) for s in unique(structs.values()))), - "", - f"class {name}(algopy.arc4.ARC4Client, typing.Protocol):", - *([_indent(["pass"]), ""] if not abi_methods else []), - *(_abi_method_to_signature(structs, m) for m in abi_methods), - ) - ) - - -def _abi_struct_to_class(s: ARC4Struct) -> Iterable[str]: - return ( - f"class {s.name}(algopy.arc4.Struct):", - _indent(f"{elem.name}: {_arc4_type_to_algopy_cls(elem.type)}" for elem in s.fields), - ) - - -def _abi_method_to_signature(structs: Mapping[str, ARC4Struct], m: ARC4ABIMethod) -> str: - try: - output_struct = structs["output"] - except KeyError: - return_type = _arc4_type_to_algopy_cls(m.returns.type_) - else: - return_type = output_struct.name - - return _indent( - ( - _arc4_method_to_decorator(m), - f"def {m.name}(", - _indent( - ( - "self,", - *(_abi_arg(arg, structs.get(arg.struct or "")) for arg in m.args), - ) - ), - f") -> {return_type}: ...", - "", - ) - ) - - -def _abi_arg(arg: ARC4MethodArg, struct: ARC4Struct | None) -> str: - python_type = struct.name if struct else _arc4_type_to_algopy_cls(arg.type_) - return f"{arg.name}: {python_type}," - - -def _arc4_type_to_algopy_cls(typ: str) -> str: - return str(arc4_to_pytype(typ)) - - -def _arc4_method_to_decorator(method: ARC4ABIMethod) -> str: - config = method.config - abimethod_args = dict[str, object]() - if config.name and config.name != method.name: - abimethod_args["name"] = config.name - if config.readonly: - abimethod_args["readonly"] = True - if config.default_args: - abimethod_args["default_args"] = dict(config.default_args) - if config.allowed_completion_types != (OnCompletionAction.NoOp,): - abimethod_args["allow_actions"] = [oca.name for oca in config.allowed_completion_types] - match config.create: - case ARC4CreateOption.allow: - abimethod_args["create"] = "allow" - case ARC4CreateOption.require: - abimethod_args["create"] = "require" - case ARC4CreateOption.disallow: - pass - case invalid: - typing.assert_never(invalid) - kwargs = ", ".join(f"{name}={value!r}" for name, value in abimethod_args.items()) - decorator = f"@{constants.ABIMETHOD_DECORATOR_ALIAS}" - if kwargs: - decorator += f"({kwargs})" - return decorator - - -def _indent(lines: Iterable[str] | str) -> str: - if not isinstance(lines, str): - lines = "\n".join(lines) - return textwrap.indent(lines, _INDENT) diff --git a/src/puyapy/awst_build/arc4_client_gen.py b/src/puyapy/awst_build/arc4_client_gen.py new file mode 100644 index 0000000000..b50857a715 --- /dev/null +++ b/src/puyapy/awst_build/arc4_client_gen.py @@ -0,0 +1,212 @@ +import textwrap +from collections.abc import Iterable, Sequence +from pathlib import Path + +from puya import ( + arc56_models as arc56, + log, +) +from puya.errors import CodeError +from puya.models import ( + OnCompletionAction, +) +from puya.utils import make_path_relative_to_cwd + +from puyapy.awst_build import constants +from puyapy.awst_build.arc4_utils import arc4_to_pytype + +logger = log.get_logger(__name__) + +_AUTO_GENERATED_COMMENT = "# This file is auto-generated, do not modify" +_INDENT = " " * 4 + + +def write_arc4_client(contract: arc56.Contract, out_dir: Path) -> None: + stub_path = out_dir / f"client_{contract.name}.py" + if _can_overwrite_auto_generated_file(stub_path): + logger.info(f"writing {make_path_relative_to_cwd(stub_path)}") + stub_text = _ClientGenerator.generate(contract) + stub_path.write_text(stub_text) + else: + logger.error( + f"Not outputting {make_path_relative_to_cwd(stub_path)} " + "since content does not appear to be auto-generated" + ) + + +def _can_overwrite_auto_generated_file(path: Path) -> bool: + return not path.exists() or path.read_text().startswith(_AUTO_GENERATED_COMMENT) + + +class _ClientGenerator: + + def __init__(self, contract: arc56.Contract): + self.contract = contract + self.python_methods = set[str]() + self.struct_to_class = dict[str, str]() + self.reserved_class_names = {contract.name} + self.reserved_method_names = set[str]() + self.class_decls = list[str]() + + @classmethod + def generate(cls, contract: arc56.Contract) -> str: + return cls(contract)._gen() # noqa: SLF001 + + def _gen(self) -> str: + # generate class definitions for any referenced structs in methods + # don't generate from self.contract.structs as it may contain other struct definitions + for method in self.contract.methods: + for struct in filter(None, (method.returns.struct, *(a.struct for a in method.args))): + if struct not in self.struct_to_class and ( + struct_def := self.contract.structs.get(struct) + ): + self._prepare_struct_class(struct, struct_def) + return "\n".join( + ( + _AUTO_GENERATED_COMMENT, + "# flake8: noqa", # this works for flake8 and ruff + "# fmt: off", # disable formatting" + "import typing", + "", + "import algopy", + "", + *self.class_decls, + "", + f"class {self.contract.name}(algopy.arc4.ARC4Client, typing.Protocol):", + # TODO: include docstring + *self._gen_methods(), + ) + ) + + def _prepare_struct_class(self, name: str, fields: Sequence[arc56.StructField]) -> str: + python_name = self._unique_class(name) + self.struct_to_class[name] = python_name + lines = [f"class {python_name}(algopy.arc4.Struct):"] + # TODO: include docstring + for field in fields: + if isinstance(field.type, str): + typ = self._get_client_type(field.type) + else: + # generate anonymous struct type + anon_struct = f"{name}_{field.name}" + typ = self._prepare_struct_class(anon_struct, field.type) + lines.append(_indent(f"{field.name}: {typ}")) + # TODO: include docstring + if self.class_decls: + self.class_decls.append("") + self.class_decls.extend(lines) + return python_name + + def _get_client_type(self, typ: str) -> str: + # map ABI / AVM type to algopy type + if typ == arc56.AVMType.uint64: + return "algopy.UInt64" + elif typ == arc56.AVMType.bytes: + return "algopy.Bytes" + elif struct := self.contract.structs.get(typ): + try: + # use existing definition + return self.struct_to_class[typ] + except KeyError: + # generate and return class name + return self._prepare_struct_class(typ, struct) + else: + return str(arc4_to_pytype(typ, None)) + + def _unique_class(self, name: str) -> str: + base_name = name + seq = 1 + while name in self.reserved_class_names: + seq += 1 + name = f"{base_name}{seq}" + + self.reserved_class_names.add(name) + return name + + def _unique_method(self, name: str) -> str: + base_name = name + seq = 1 + while name in self.reserved_method_names: + seq += 1 + name = f"{base_name}{seq}" + + self.reserved_method_names.add(name) + return name + + def _gen_methods(self) -> Iterable[str]: + if not self.contract.methods: + yield _indent("pass") + yield "" + else: + for method in self.contract.methods: + yield self._gen_method(method) + + def _gen_method(self, method: arc56.Method) -> str: + return_type = self._get_client_type(method.returns.struct or method.returns.type) + python_method = self._unique_method(method.name) + return _indent( + ( + _arc4_method_to_decorator(python_method, method), + f"def {python_method}(", + # TODO: include docstring + _indent( + ( + "self,", + *(self._gen_arg(arg) for arg in method.args), + ) + ), + f") -> {return_type}: ...", + "", + ) + ) + + def _gen_arg(self, arg: arc56.MethodArg) -> str: + python_type = self._get_client_type(arg.struct or arg.type) + return f"{arg.name}: {python_type}," + + +def _arc4_method_to_decorator(python_method: str, method: arc56.Method) -> str: + abimethod_args = dict[str, object]() + if method.name != python_method: + abimethod_args["name"] = method.name + if method.readonly: + abimethod_args["readonly"] = True + if not _compatible_actions(method.actions.create, method.actions.call): + # TODO: support this, once decorators support it + raise CodeError( + f"unsupported on completion combination for generating an ARC4 client" + f" for method: {method.name}" + ) + actions = sorted( + {*method.actions.create, *method.actions.call}, key=lambda a: OnCompletionAction[a] + ) + if set(actions) != {OnCompletionAction.NoOp.name}: + abimethod_args["allow_actions"] = actions + if method.actions.create and method.actions.call: + abimethod_args["create"] = "allow" + elif method.actions.create: + abimethod_args["create"] = "require" + else: + # disallow is default + pass + kwargs = ", ".join(f"{name}={value!r}" for name, value in abimethod_args.items()) + decorator = f"@{constants.ABIMETHOD_DECORATOR_ALIAS}" + if kwargs: + decorator += f"({kwargs})" + return decorator + + +def _compatible_actions(create: Sequence[str], call: Sequence[str]) -> bool: + if not create: + return True + if not call: + return True + # if both collections are present, then they are compatible if everything in + # create is also in call + return all(a in call for a in create) + + +def _indent(lines: Iterable[str] | str) -> str: + if not isinstance(lines, str): + lines = "\n".join(lines) + return textwrap.indent(lines, _INDENT) diff --git a/src/puyapy/client_gen.py b/src/puyapy/client_gen.py index 8b96408ba2..74503b4086 100644 --- a/src/puyapy/client_gen.py +++ b/src/puyapy/client_gen.py @@ -1,28 +1,24 @@ import argparse import json import typing -from collections.abc import Iterable, Mapping, Sequence +from collections.abc import Iterable, Sequence from pathlib import Path import attrs -from immutabledict import immutabledict -from puya import log +from cattrs.preconf.json import make_converter +from puya import ( + arc56_models as arc56, + log, +) from puya.arc32 import OCA_ARC32_MAPPING +from puya.arc56 import allowed_call_oca, allowed_create_oca from puya.errors import PuyaError from puya.models import ( - ARC4ABIMethod, - ARC4ABIMethodConfig, - ARC4CreateOption, - ARC4Method, ARC4MethodArg, ARC4Returns, - ARC4Struct, - ARC4StructField, - OnCompletionAction, ) -from puya.parse import SourceLocation -from puyapy.awst_build.arc32_client_gen import write_arc32_client +from puyapy.awst_build.arc4_client_gen import write_arc4_client logger = log.get_logger(__name__) ARC32_OCA_MAPPING = {v: k for k, v in OCA_ARC32_MAPPING.items()} @@ -32,6 +28,7 @@ class PuyaGenOptions: paths: Sequence[Path] = attrs.field(default=(), repr=lambda p: str(list(map(str, p)))) log_level: log.LogLevel = log.LogLevel.info + out_dir: Path | None = None def main() -> None: @@ -39,25 +36,45 @@ def main() -> None: prog="puyapy-clientgen", formatter_class=argparse.ArgumentDefaultsHelpFormatter, description="Output algopy contract client for typed ARC4 ABI calls from an " - "ARC32 application.json spec", + "ARC-32 or ARC-56 application spec", + ) + parser.add_argument( + "--out-dir", + type=Path, + help="Path for outputting client, defaults to app spec folder", + default=False, ) parser.add_argument("paths", type=Path, nargs="+", metavar="PATH") options = PuyaGenOptions() parser.parse_args(namespace=options) log.configure_logging(min_log_level=options.log_level) - output_stubs(options.paths) + output_stubs(options.paths, options.out_dir) -def output_stubs(paths: Sequence[Path]) -> None: +def output_stubs(paths: Sequence[Path], out_dir: Path | None) -> None: try: app_spec_paths = resolve_app_specs(paths) for app_spec_path in app_spec_paths: - name, structs, methods = parse_app_spec_methods(app_spec_path.read_text("utf8")) - write_arc32_client(name, structs, methods, app_spec_path.parent) + app_spec_json = app_spec_path.read_text("utf8") + if app_spec_path.name.endswith(".arc56.json"): + app_spec = parse_arc56(app_spec_json) + else: + # TODO: use algokit_utils to do this conversion when it is available? + app_spec = _convert_arc32_to_arc56(app_spec_json) + write_arc4_client(app_spec, out_dir or app_spec_path.parent) except PuyaError as ex: logger.error(str(ex)) # noqa: TRY400 +def _convert_arc32_to_arc56(app_spec_json: str) -> arc56.Contract: + name, structs, methods = _parse_arc32_app_spec_methods(app_spec_json) + return arc56.Contract( + name=name, + structs=structs, + methods=methods, + ) + + def resolve_app_specs(paths: Sequence[Path]) -> Sequence[Path]: app_specs = list[Path]() for path in paths: @@ -76,50 +93,55 @@ def resolve_app_specs(paths: Sequence[Path]) -> Sequence[Path]: return app_specs -def parse_app_spec_methods( +def parse_arc56(app_spec_json: str) -> arc56.Contract: + converter = make_converter(omit_if_default=True) + return converter.loads(app_spec_json, arc56.Contract) + + +def _parse_arc32_app_spec_methods( app_spec_json: str, -) -> tuple[str, dict[str, ARC4Struct], Sequence[ARC4Method]]: +) -> tuple[str, dict[str, Sequence[arc56.StructField]], Sequence[arc56.Method]]: + # only need to parse a limited subset of ARC32 for client generation + # i.e. ABI methods, their OCA parameters, and any struct info + # default args are ignored as they aren't supported for on chain calls currently + app_spec = json.loads(app_spec_json) contract = app_spec["contract"] hints = app_spec["hints"] contract_name = contract["name"] - methods = list[ARC4Method]() + methods = list[arc56.Method]() arc4_methods = {m.signature: m for m in _parse_methods(contract["methods"])} - known_structs = dict[str, ARC4Struct]() + known_structs = dict[str, Sequence[arc56.StructField]]() for arc4_method in arc4_methods.values(): method_hints = hints[str(arc4_method.signature)] - create_option, allowed_oca = _call_config(method_hints["call_config"]) arg_to_struct = dict[str, str]() - for param, struct in _structs(method_hints.get("structs", {})): + for param, struct_name, struct in _parse_structs(method_hints.get("structs", {})): for known_struct_name, known_struct in known_structs.items(): if known_struct == struct: arg_to_struct[param] = known_struct_name break else: - known_structs[struct.name] = struct - arg_to_struct[param] = struct.name + known_structs[struct_name] = struct + arg_to_struct[param] = struct_name methods.append( - ARC4ABIMethod( - name=arc4_method.python_name, + arc56.Method( + name=arc4_method.signature.name, desc=arc4_method.desc, args=[ - attrs.evolve(a, struct=arg_to_struct.get(a.name)) + arc56.MethodArg( + name=a.name, + type=a.type_, + struct=arg_to_struct.get(a.name), + ) for a in arc4_method.signature.args ], - returns=attrs.evolve( - arc4_method.signature.returns, struct=arg_to_struct.get("output") - ), - config=ARC4ABIMethodConfig( - source_location=SourceLocation(file=None, line=1), - name=arc4_method.signature.name, - create=create_option, - readonly=bool(method_hints.get("read_only") or arc4_method.readonly), - allowed_completion_types=allowed_oca, - default_args=immutabledict( - _default_args(method_hints.get("default_arguments", {}), arc4_methods) - ), + returns=arc56.MethodReturns( + type=arc4_method.signature.returns.type_, + desc=arc4_method.desc, + struct=arg_to_struct.get("output"), ), - events=[], # ARC-32 does not specify events + readonly=bool(method_hints.get("read_only") or arc4_method.readonly), + actions=_parse_call_config(method_hints["call_config"]), ) ) return contract_name, known_structs, methods @@ -138,22 +160,17 @@ def __str__(self) -> str: @attrs.frozen(kw_only=True) class _Method: signature: _MethodSignature - python_name: str desc: str | None readonly: bool | None def _parse_methods(methods: list[dict[str, typing.Any]]) -> Iterable[_Method]: - arc4_names = dict[str, int]() for method in methods: signature = _parse_signature(method) - name_seen = arc4_names[signature.name] = arc4_names.get(signature.name, 0) + 1 yield _Method( signature=signature, desc=method.get("desc"), readonly=method.get("readonly"), - # de-duplicate potential collisions - python_name=signature.name if name_seen == 1 else f"{signature.name}{name_seen}", ) @@ -178,53 +195,36 @@ def _parse_signature(method: dict[str, typing.Any]) -> _MethodSignature: ) -def _call_config( - method_call_config: dict[str, str] -) -> tuple[ARC4CreateOption, Sequence[OnCompletionAction]]: - try: - (call_config,) = set(method_call_config.values()) - except ValueError as ex: - raise PuyaError("Different call configs for a single method not supported") from ex - match call_config: - case "CREATE": - create_option = ARC4CreateOption.require - case "ALL": - create_option = ARC4CreateOption.allow - case "CALL": - create_option = ARC4CreateOption.disallow - case invalid: - raise PuyaError(f"invalid call config option: {invalid}") - allowed_oca = [ARC32_OCA_MAPPING[a] for a in method_call_config] - return create_option, allowed_oca - - -def _default_args( - default_args: dict[str, typing.Any], arc32_python_sig: Mapping[_MethodSignature, _Method] -) -> Iterable[tuple[str, str]]: - for param, default_arg_config in default_args.items(): - data = default_arg_config["data"] - source = default_arg_config["source"] - match source: - case "global-state": - yield param, data - case "local-state": - yield param, data - case "abi-method": - signature = _parse_signature(data) - yield param, arc32_python_sig[signature].python_name - case _: - raise PuyaError(f"Unsupported source '{source}' for default argument: {param}") - - -def _structs(structs: dict[str, dict[str, typing.Any]]) -> Iterable[tuple[str, ARC4Struct]]: +def _parse_call_config(method_call_config: dict[str, str]) -> arc56.MethodActions: + create = [] + call = [] + for oca, call_config in method_call_config.items(): + action = ARC32_OCA_MAPPING[oca].name + match call_config: + case "CREATE" if allowed_create_oca(action): + create.append(action) + case "CALL" if allowed_call_oca(action): + call.append(action) + # allowed creates is narrower than calls so only need to check that + case "ALL" if allowed_create_oca(action): + create.append(action) + call.append(action) + case invalid: + raise PuyaError(f"invalid call config option: {invalid}") + + return arc56.MethodActions( + create=create, + call=call, + ) + + +def _parse_structs( + structs: dict[str, dict[str, typing.Any]] +) -> Iterable[tuple[str, str, Sequence[arc56.StructField]]]: for param, struct_config in structs.items(): - yield param, ARC4Struct( - fullname=struct_config["name"], - fields=[ - ARC4StructField(name=f[0], type=f[1], struct=None) - for f in struct_config["elements"] - ], - ) + yield param, struct_config["name"], [ + arc56.StructField(name=f[0], type=f[1]) for f in struct_config["elements"] + ] if __name__ == "__main__": diff --git a/src/puyapy/compile.py b/src/puyapy/compile.py index 584eb9acf4..3bc0fbd3e2 100644 --- a/src/puyapy/compile.py +++ b/src/puyapy/compile.py @@ -16,7 +16,7 @@ import mypy.util from packaging import version from puya import log, models -from puya.arc32 import create_arc32_json +from puya.arc56 import create_arc56_json from puya.awst.nodes import AWST from puya.awst.serialize import awst_to_json from puya.awst.to_code_visitor import ToCodeVisitor @@ -24,9 +24,9 @@ from puya.errors import log_exceptions from puya.utils import make_path_relative_to_cwd -from puyapy.awst_build.arc32_client_gen import write_arc32_client +from puyapy.awst_build.arc4_client_gen import write_arc4_client from puyapy.awst_build.main import transform_ast -from puyapy.client_gen import parse_app_spec_methods +from puyapy.client_gen import parse_arc56 from puyapy.options import PuyaPyOptions from puyapy.parse import TYPESHED_PATH, ParseResult, parse_and_typecheck from puyapy.utils import determine_out_dir @@ -79,12 +79,13 @@ def compile_to_teal(puyapy_options: PuyaPyOptions) -> None: ) log_ctx.exit_if_errors() if puyapy_options.output_client: - write_arc32_clients(compilation_set, teal) + write_arc32_clients(puyapy_options.template_vars_prefix, compilation_set, teal) # needs to be outside the with block log_ctx.exit_if_errors() def write_arc32_clients( + template_prefix: str, compilation_set: Mapping[models.ContractReference | models.LogicSigReference, Path], artifacts: Sequence[models.CompilationArtifact], ) -> None: @@ -92,16 +93,17 @@ def write_arc32_clients( if isinstance(artifact, models.CompiledContract) and artifact.metadata.is_arc4: contract_out_dir = compilation_set.get(artifact.id) if contract_out_dir: - app_spec_json = create_arc32_json( - artifact.approval_program.teal_src, - artifact.clear_program.teal_src, - artifact.metadata, + app_spec_json = create_arc56_json( + approval_program=artifact.approval_program, + clear_program=artifact.clear_program, + metadata=artifact.metadata, + template_prefix=template_prefix, ) - # use round trip of ARC32 -> reparse to ensure consistency - # of client output regardless if generating from ARC32 or + # use round trip of ARC-56 -> reparse to ensure consistency + # of client output regardless if generating from ARC-56 or # Puya ARC4Contract - name, structs, methods = parse_app_spec_methods(app_spec_json) - write_arc32_client(name, structs, methods, contract_out_dir) + contract = parse_arc56(app_spec_json) + write_arc4_client(contract, contract_out_dir) def parse_with_mypy(paths: Sequence[Path]) -> ParseResult: diff --git a/test_cases/abi_routing/out/CustomApproval.arc56.json b/test_cases/abi_routing/out/CustomApproval.arc56.json index 20e6b31957..080b15c971 100644 --- a/test_cases/abi_routing/out/CustomApproval.arc56.json +++ b/test_cases/abi_routing/out/CustomApproval.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "CustomApproval", "structs": {}, "methods": [ @@ -28,6 +24,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -56,9 +57,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -109,5 +107,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/abi_routing/out/MinimumARC4.arc56.json b/test_cases/abi_routing/out/MinimumARC4.arc56.json index 8e08aa78b2..2c9a802a15 100644 --- a/test_cases/abi_routing/out/MinimumARC4.arc56.json +++ b/test_cases/abi_routing/out/MinimumARC4.arc56.json @@ -1,11 +1,12 @@ { + "name": "MinimumARC4", + "structs": {}, + "methods": [], "arcs": [ 22, 28 ], - "name": "MinimumARC4", - "structs": {}, - "methods": [], + "networks": {}, "state": { "schema": { "global": { @@ -40,9 +41,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -75,5 +73,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/abi_routing/out/Reference.arc56.json b/test_cases/abi_routing/out/Reference.arc56.json index adecb15394..5e44f70d37 100644 --- a/test_cases/abi_routing/out/Reference.arc56.json +++ b/test_cases/abi_routing/out/Reference.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Reference", "structs": {}, "methods": [ @@ -28,7 +24,7 @@ "recommendations": {} }, { - "name": "full_abi_config", + "name": "all_the_things", "args": [ { "type": "uint64", @@ -191,7 +187,7 @@ "recommendations": {} }, { - "name": "get_app", + "name": "get_application", "args": [], "returns": { "type": "uint64" @@ -207,7 +203,7 @@ "recommendations": {} }, { - "name": "get_a_int", + "name": "get_an_int", "args": [], "returns": { "type": "uint64" @@ -390,9 +386,9 @@ ] }, "readonly": false, + "desc": "Fifteen args should not encode the last argument as a tuple", "events": [], - "recommendations": {}, - "desc": "Fifteen args should not encode the last argument as a tuple" + "recommendations": {} }, { "name": "method_with_more_than_15_args", @@ -512,9 +508,9 @@ ] }, "readonly": false, + "desc": "Application calls only support 16 args, and arc4 calls utilise the first arg for the method\nselector. Args beyond this number are packed into a tuple and placed in the 16th slot.", "events": [], - "recommendations": {}, - "desc": "Application calls only support 16 args, and arc4 calls utilise the first arg for the method\nselector. Args beyond this number are packed into a tuple and placed in the 16th slot." + "recommendations": {} }, { "name": "hello_with_algopy_string", @@ -538,6 +534,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -594,9 +595,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -794,5 +792,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/abi_routing/out/client_Reference.py b/test_cases/abi_routing/out/client_Reference.py index 03b489afb7..9c1b3d4e78 100644 --- a/test_cases/abi_routing/out/client_Reference.py +++ b/test_cases/abi_routing/out/client_Reference.py @@ -67,7 +67,7 @@ def get_an_int( self, ) -> algopy.arc4.UIntN[typing.Literal[64]]: ... - @algopy.arc4.abimethod(default_args={'asset_from_storage': 'asa', 'asset_from_function': 'get_asset', 'account_from_storage': 'creator', 'account_from_function': 'get_address', 'application_from_storage': 'app', 'application_from_function': 'get_application', 'bytes_from_storage': 'some_bytes', 'int_from_storage': 'an_int', 'int_from_function': 'get_an_int'}) + @algopy.arc4.abimethod def method_with_default_args( self, asset_from_storage: algopy.Asset, diff --git a/test_cases/arc4_dynamic_arrays/out/DynamicArrayContract.arc56.json b/test_cases/arc4_dynamic_arrays/out/DynamicArrayContract.arc56.json index 132512d32a..c958f96c26 100644 --- a/test_cases/arc4_dynamic_arrays/out/DynamicArrayContract.arc56.json +++ b/test_cases/arc4_dynamic_arrays/out/DynamicArrayContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "DynamicArrayContract", "structs": {}, "methods": [ @@ -103,6 +99,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -131,9 +132,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -206,5 +204,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/arc4_types/out/Arc4DynamicStringArrayContract.arc56.json b/test_cases/arc4_types/out/Arc4DynamicStringArrayContract.arc56.json index 7a986bc2c4..7a65d2c54d 100644 --- a/test_cases/arc4_types/out/Arc4DynamicStringArrayContract.arc56.json +++ b/test_cases/arc4_types/out/Arc4DynamicStringArrayContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Arc4DynamicStringArrayContract", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -67,9 +68,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +114,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/arc_56/out/Contract.arc56.json b/test_cases/arc_56/out/Contract.arc56.json index 01a8584e52..24cf54709c 100644 --- a/test_cases/arc_56/out/Contract.arc56.json +++ b/test_cases/arc_56/out/Contract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Contract", "structs": { "EventOnly": [ @@ -122,7 +118,7 @@ "recommendations": {} }, { - "name": "errors", + "name": "emits_error", "args": [ { "type": "(uint64,string,(byte[],uint8))", @@ -242,6 +238,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -349,70 +350,6 @@ "OptIn" ] }, - "events": [ - { - "name": "SharedStruct", - "args": [ - { - "type": "byte[]", - "name": "foo" - }, - { - "type": "uint8", - "name": "bar" - } - ], - "desc": "This struct is shared" - }, - { - "name": "Anonymous", - "args": [ - { - "type": "string", - "name": "field1" - }, - { - "type": "(byte[],uint8)", - "name": "field2", - "struct": "SharedStruct" - } - ] - }, - { - "name": "Anonymous2", - "args": [ - { - "type": "(uint64,uint64)", - "name": "field1", - "struct": "EventOnly" - }, - { - "type": "(byte[],uint8)", - "name": "field2", - "struct": "SharedStruct" - } - ] - } - ], - "templateVariables": { - "STRUCT": { - "type": "SharedStruct", - "value": "AAL/AAJIaQ==" - }, - "AVM_UINT64": { - "type": "AVMUint64", - "value": "AAAAAAAAAHs=" - }, - "AVM_STRING": { - "type": "AVMString", - "value": "SGVsbG8=" - }, - "ARC4_UINT8": { - "type": "uint8", - "value": "/w==" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -491,5 +428,68 @@ "minor": 99, "patch": 99 } + }, + "events": [ + { + "name": "SharedStruct", + "args": [ + { + "type": "byte[]", + "name": "foo" + }, + { + "type": "uint8", + "name": "bar" + } + ], + "desc": "This struct is shared" + }, + { + "name": "Anonymous", + "args": [ + { + "type": "string", + "name": "field1" + }, + { + "type": "(byte[],uint8)", + "name": "field2", + "struct": "SharedStruct" + } + ] + }, + { + "name": "Anonymous2", + "args": [ + { + "type": "(uint64,uint64)", + "name": "field1", + "struct": "EventOnly" + }, + { + "type": "(byte[],uint8)", + "name": "field2", + "struct": "SharedStruct" + } + ] + } + ], + "templateVariables": { + "STRUCT": { + "type": "SharedStruct", + "value": "AAL/AAJIaQ==" + }, + "AVM_UINT64": { + "type": "AVMUint64", + "value": "AAAAAAAAAHs=" + }, + "AVM_STRING": { + "type": "AVMString", + "value": "SGVsbG8=" + }, + "ARC4_UINT8": { + "type": "uint8", + "value": "/w==" + } } } \ No newline at end of file diff --git a/test_cases/arc_56/out/client_Contract.py b/test_cases/arc_56/out/client_Contract.py index 5598046888..767d3f8567 100644 --- a/test_cases/arc_56/out/client_Contract.py +++ b/test_cases/arc_56/out/client_Contract.py @@ -5,14 +5,15 @@ import algopy -class TopLevelStruct(algopy.arc4.Struct): - a: algopy.arc4.UIntN[typing.Literal[64]] - b: algopy.arc4.String - shared: algopy.arc4.Tuple[algopy.arc4.DynamicBytes, algopy.arc4.UIntN[typing.Literal[8]]] class SharedStruct(algopy.arc4.Struct): foo: algopy.arc4.DynamicBytes bar: algopy.arc4.UIntN[typing.Literal[8]] +class TopLevelStruct(algopy.arc4.Struct): + a: algopy.arc4.UIntN[typing.Literal[64]] + b: algopy.arc4.String + shared: SharedStruct + class Contract(algopy.arc4.ARC4Client, typing.Protocol): @algopy.arc4.abimethod(allow_actions=['NoOp', 'OptIn'], create='allow') def create( @@ -30,7 +31,7 @@ def struct_arg( def struct_return( self, arg: TopLevelStruct, - ) -> algopy.arc4.Tuple[algopy.arc4.DynamicBytes, algopy.arc4.UIntN[typing.Literal[8]]]: ... + ) -> SharedStruct: ... @algopy.arc4.abimethod(readonly=True) def emits_error( diff --git a/test_cases/avm_types_in_abi/out/TestContract.arc56.json b/test_cases/avm_types_in_abi/out/TestContract.arc56.json index 863587c580..b092b842e0 100644 --- a/test_cases/avm_types_in_abi/out/TestContract.arc56.json +++ b/test_cases/avm_types_in_abi/out/TestContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "TestContract", "structs": {}, "methods": [ @@ -69,6 +65,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -95,9 +96,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -149,5 +147,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/compile/out/Hello.arc56.json b/test_cases/compile/out/Hello.arc56.json index 265d94410e..6b753576ba 100644 --- a/test_cases/compile/out/Hello.arc56.json +++ b/test_cases/compile/out/Hello.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Hello", "structs": {}, "methods": [ @@ -65,6 +61,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -99,9 +100,6 @@ "UpdateApplication" ] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -161,5 +159,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/compile/out/HelloBase.arc56.json b/test_cases/compile/out/HelloBase.arc56.json index 8d01e8acde..8b413d2d56 100644 --- a/test_cases/compile/out/HelloBase.arc56.json +++ b/test_cases/compile/out/HelloBase.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloBase", "structs": {}, "methods": [ @@ -44,6 +40,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -80,9 +81,6 @@ "UpdateApplication" ] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -141,5 +139,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/compile/out/HelloFactory.arc56.json b/test_cases/compile/out/HelloFactory.arc56.json index e24557a4f9..62f3ad9739 100644 --- a/test_cases/compile/out/HelloFactory.arc56.json +++ b/test_cases/compile/out/HelloFactory.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloFactory", "structs": {}, "methods": [ @@ -215,6 +211,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -243,9 +244,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -341,5 +339,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/compile/out/HelloOtherConstants.arc56.json b/test_cases/compile/out/HelloOtherConstants.arc56.json index b2d0531151..3778260e34 100644 --- a/test_cases/compile/out/HelloOtherConstants.arc56.json +++ b/test_cases/compile/out/HelloOtherConstants.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloOtherConstants", "structs": {}, "methods": [ @@ -60,6 +56,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -107,26 +108,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": { - "TMPL_GREETING": { - "type": "AVMString", - "value": "dG1wbA==" - }, - "TMPL_NUM": { - "type": "AVMBytes", - "value": "Qg==" - }, - "TMPL_ACCOUNT": { - "type": "AVMBytes", - "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - }, - "TMPL_METHOD": { - "type": "AVMBytes", - "value": "AAAAAA==" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -204,5 +185,24 @@ "minor": 99, "patch": 99 } + }, + "events": [], + "templateVariables": { + "TMPL_GREETING": { + "type": "AVMString", + "value": "dG1wbA==" + }, + "TMPL_NUM": { + "type": "AVMBytes", + "value": "Qg==" + }, + "TMPL_ACCOUNT": { + "type": "AVMBytes", + "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + "TMPL_METHOD": { + "type": "AVMBytes", + "value": "AAAAAA==" + } } } \ No newline at end of file diff --git a/test_cases/compile/out/HelloPrfx.arc56.json b/test_cases/compile/out/HelloPrfx.arc56.json index a778484feb..219fc7340e 100644 --- a/test_cases/compile/out/HelloPrfx.arc56.json +++ b/test_cases/compile/out/HelloPrfx.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloPrfx", "structs": {}, "methods": [ @@ -60,6 +56,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -94,14 +95,6 @@ "UpdateApplication" ] }, - "events": [], - "templateVariables": { - "PRFX_GREETING": { - "type": "AVMString", - "value": "cHJmeA==" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -161,5 +154,12 @@ "minor": 99, "patch": 99 } + }, + "events": [], + "templateVariables": { + "PRFX_GREETING": { + "type": "AVMString", + "value": "cHJmeA==" + } } } \ No newline at end of file diff --git a/test_cases/compile/out/HelloTmpl.arc56.json b/test_cases/compile/out/HelloTmpl.arc56.json index 8e3f9a95eb..72a3f5052e 100644 --- a/test_cases/compile/out/HelloTmpl.arc56.json +++ b/test_cases/compile/out/HelloTmpl.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "HelloTmpl", "structs": {}, "methods": [ @@ -60,6 +56,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -94,14 +95,6 @@ "UpdateApplication" ] }, - "events": [], - "templateVariables": { - "TMPL_GREETING": { - "type": "AVMString", - "value": "dG1wbA==" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -161,5 +154,12 @@ "minor": 99, "patch": 99 } + }, + "events": [], + "templateVariables": { + "TMPL_GREETING": { + "type": "AVMString", + "value": "dG1wbA==" + } } } \ No newline at end of file diff --git a/test_cases/compile/out/LargeProgram.arc56.json b/test_cases/compile/out/LargeProgram.arc56.json index a90fcc23fc..1848b468bf 100644 --- a/test_cases/compile/out/LargeProgram.arc56.json +++ b/test_cases/compile/out/LargeProgram.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "LargeProgram", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -67,9 +68,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -121,5 +119,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/debug/out/DebugContract.arc56.json b/test_cases/debug/out/DebugContract.arc56.json index 929a5f7ea5..9a23342132 100644 --- a/test_cases/debug/out/DebugContract.arc56.json +++ b/test_cases/debug/out/DebugContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "DebugContract", "structs": {}, "methods": [ @@ -36,6 +32,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -64,14 +65,6 @@ ], "call": [] }, - "events": [], - "templateVariables": { - "A_MULT": { - "type": "AVMUint64", - "value": "AAAAAAAAAAE=" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +109,12 @@ "minor": 99, "patch": 99 } + }, + "events": [], + "templateVariables": { + "A_MULT": { + "type": "AVMUint64", + "value": "AAAAAAAAAAE=" + } } } \ No newline at end of file diff --git a/test_cases/diamond_mro/out/Base1.arc56.json b/test_cases/diamond_mro/out/Base1.arc56.json index 96b389a213..be6aaebf20 100644 --- a/test_cases/diamond_mro/out/Base1.arc56.json +++ b/test_cases/diamond_mro/out/Base1.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Base1", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -65,9 +66,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -113,5 +111,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/diamond_mro/out/Base2.arc56.json b/test_cases/diamond_mro/out/Base2.arc56.json index 626f18d0c0..4769f922b2 100644 --- a/test_cases/diamond_mro/out/Base2.arc56.json +++ b/test_cases/diamond_mro/out/Base2.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Base2", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -65,9 +66,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -113,5 +111,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/diamond_mro/out/Derived.arc56.json b/test_cases/diamond_mro/out/Derived.arc56.json index 6494a56622..37e4451bf7 100644 --- a/test_cases/diamond_mro/out/Derived.arc56.json +++ b/test_cases/diamond_mro/out/Derived.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Derived", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -65,9 +66,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -113,5 +111,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/diamond_mro/out/GP.arc56.json b/test_cases/diamond_mro/out/GP.arc56.json index 12f54fde59..0068486f7e 100644 --- a/test_cases/diamond_mro/out/GP.arc56.json +++ b/test_cases/diamond_mro/out/GP.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "GP", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -65,9 +66,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -113,5 +111,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/everything/out/MyContract.arc56.json b/test_cases/everything/out/MyContract.arc56.json index ad5cf8e820..1426a3fce2 100644 --- a/test_cases/everything/out/MyContract.arc56.json +++ b/test_cases/everything/out/MyContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "MyContract", "structs": {}, "methods": [ @@ -102,6 +98,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -145,9 +146,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -229,5 +227,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/group_side_effects/out/AppCall.arc56.json b/test_cases/group_side_effects/out/AppCall.arc56.json index be0f172b88..59a38e68cd 100644 --- a/test_cases/group_side_effects/out/AppCall.arc56.json +++ b/test_cases/group_side_effects/out/AppCall.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "AppCall", "structs": {}, "methods": [ @@ -23,6 +19,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -51,9 +52,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -98,5 +96,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/group_side_effects/out/AppExpectingEffects.arc56.json b/test_cases/group_side_effects/out/AppExpectingEffects.arc56.json index 6064bc9e45..1a51faf73d 100644 --- a/test_cases/group_side_effects/out/AppExpectingEffects.arc56.json +++ b/test_cases/group_side_effects/out/AppExpectingEffects.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "AppExpectingEffects", "structs": {}, "methods": [ @@ -53,6 +49,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -81,9 +82,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -185,5 +183,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/inner_transactions/out/ArrayAccessContract.arc56.json b/test_cases/inner_transactions/out/ArrayAccessContract.arc56.json index ec56e4b683..bb8bf3ced6 100644 --- a/test_cases/inner_transactions/out/ArrayAccessContract.arc56.json +++ b/test_cases/inner_transactions/out/ArrayAccessContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "ArrayAccessContract", "structs": {}, "methods": [ @@ -28,6 +24,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -56,9 +57,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -123,5 +121,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/inner_transactions/out/CreateAndTransferContract.arc56.json b/test_cases/inner_transactions/out/CreateAndTransferContract.arc56.json index 0f6b9353d3..793cef198e 100644 --- a/test_cases/inner_transactions/out/CreateAndTransferContract.arc56.json +++ b/test_cases/inner_transactions/out/CreateAndTransferContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "CreateAndTransferContract", "structs": {}, "methods": [ @@ -23,6 +19,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -51,9 +52,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -104,5 +102,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/inner_transactions/out/FieldTupleContract.arc56.json b/test_cases/inner_transactions/out/FieldTupleContract.arc56.json index 2119fe90fa..fe224e6cfa 100644 --- a/test_cases/inner_transactions/out/FieldTupleContract.arc56.json +++ b/test_cases/inner_transactions/out/FieldTupleContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "FieldTupleContract", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -67,9 +68,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +114,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/inner_transactions/out/Greeter.arc56.json b/test_cases/inner_transactions/out/Greeter.arc56.json index a626b3d471..b79d2ef010 100644 --- a/test_cases/inner_transactions/out/Greeter.arc56.json +++ b/test_cases/inner_transactions/out/Greeter.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Greeter", "structs": {}, "methods": [ @@ -44,6 +40,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -78,9 +79,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -147,5 +145,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/inner_transactions_assignment/out/Contract.arc56.json b/test_cases/inner_transactions_assignment/out/Contract.arc56.json index b10e5ecc19..ca476c43b6 100644 --- a/test_cases/inner_transactions_assignment/out/Contract.arc56.json +++ b/test_cases/inner_transactions_assignment/out/Contract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Contract", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -67,9 +68,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +114,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/named_tuples/out/NamedTuplesContract.arc56.json b/test_cases/named_tuples/out/NamedTuplesContract.arc56.json index 2df682cf9e..4169e3d534 100644 --- a/test_cases/named_tuples/out/NamedTuplesContract.arc56.json +++ b/test_cases/named_tuples/out/NamedTuplesContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "NamedTuplesContract", "structs": { "TestTuple": [ @@ -82,6 +78,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -110,9 +111,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -165,5 +163,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/named_tuples/out/client_NamedTuplesContract.py b/test_cases/named_tuples/out/client_NamedTuplesContract.py index 9d160eef60..8e34976dad 100644 --- a/test_cases/named_tuples/out/client_NamedTuplesContract.py +++ b/test_cases/named_tuples/out/client_NamedTuplesContract.py @@ -19,7 +19,7 @@ def build_tuple( b: algopy.arc4.BigUIntN[typing.Literal[512]], c: algopy.arc4.String, d: algopy.arc4.DynamicBytes, - ) -> algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.BigUIntN[typing.Literal[512]], algopy.arc4.String, algopy.arc4.DynamicBytes]: ... + ) -> TestTuple: ... @algopy.arc4.abimethod def test_tuple( diff --git a/test_cases/regression_tests/out/Issue118.arc56.json b/test_cases/regression_tests/out/Issue118.arc56.json index d33c364741..d13c4ad1c0 100644 --- a/test_cases/regression_tests/out/Issue118.arc56.json +++ b/test_cases/regression_tests/out/Issue118.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Issue118", "structs": {}, "methods": [ @@ -28,6 +24,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -56,9 +57,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -103,5 +101,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/reinterpret_cast/out/Contract.arc56.json b/test_cases/reinterpret_cast/out/Contract.arc56.json index cc6c9cf13d..a62b77dca9 100644 --- a/test_cases/reinterpret_cast/out/Contract.arc56.json +++ b/test_cases/reinterpret_cast/out/Contract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Contract", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -67,9 +68,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +114,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/state_mutations/out/Contract.arc56.json b/test_cases/state_mutations/out/Contract.arc56.json index 66e20743ca..a5cce79fb9 100644 --- a/test_cases/state_mutations/out/Contract.arc56.json +++ b/test_cases/state_mutations/out/Contract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Contract", "structs": {}, "methods": [ @@ -55,6 +51,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -109,9 +110,6 @@ "OptIn" ] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -215,5 +213,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/state_proxies/out/StateProxyContract.arc56.json b/test_cases/state_proxies/out/StateProxyContract.arc56.json index cbeccbd483..64adb974b5 100644 --- a/test_cases/state_proxies/out/StateProxyContract.arc56.json +++ b/test_cases/state_proxies/out/StateProxyContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "StateProxyContract", "structs": {}, "methods": [ @@ -23,6 +19,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -75,9 +76,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -116,5 +114,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/state_totals/out/Contract.arc56.json b/test_cases/state_totals/out/Contract.arc56.json index bec612f320..2eaa979e89 100644 --- a/test_cases/state_totals/out/Contract.arc56.json +++ b/test_cases/state_totals/out/Contract.arc56.json @@ -1,11 +1,12 @@ { + "name": "Contract", + "structs": {}, + "methods": [], "arcs": [ 22, 28 ], - "name": "Contract", - "structs": {}, - "methods": [], + "networks": {}, "state": { "schema": { "global": { @@ -46,9 +47,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -81,5 +79,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/template_variables/out/TemplateVariablesContract.arc56.json b/test_cases/template_variables/out/TemplateVariablesContract.arc56.json index 9e9cd95452..222f1900a2 100644 --- a/test_cases/template_variables/out/TemplateVariablesContract.arc56.json +++ b/test_cases/template_variables/out/TemplateVariablesContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "TemplateVariablesContract", "structs": {}, "methods": [ @@ -39,6 +35,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -70,26 +71,6 @@ "UpdateApplication" ] }, - "events": [], - "templateVariables": { - "SOME_BYTES": { - "type": "AVMBytes", - "value": "" - }, - "SOME_BIG_UINT": { - "type": "AVMBytes", - "value": "" - }, - "UPDATABLE": { - "type": "AVMUint64", - "value": "AAAAAAAAAAA=" - }, - "DELETABLE": { - "type": "AVMUint64", - "value": "AAAAAAAAAAA=" - } - }, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -144,5 +125,24 @@ "minor": 99, "patch": 99 } + }, + "events": [], + "templateVariables": { + "SOME_BYTES": { + "type": "AVMBytes", + "value": "" + }, + "SOME_BIG_UINT": { + "type": "AVMBytes", + "value": "" + }, + "UPDATABLE": { + "type": "AVMUint64", + "value": "AAAAAAAAAAA=" + }, + "DELETABLE": { + "type": "AVMUint64", + "value": "AAAAAAAAAAA=" + } } } \ No newline at end of file diff --git a/test_cases/transaction/out/TransactionContract.arc56.json b/test_cases/transaction/out/TransactionContract.arc56.json index 2ff6446b1a..83688daaa3 100644 --- a/test_cases/transaction/out/TransactionContract.arc56.json +++ b/test_cases/transaction/out/TransactionContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "TransactionContract", "structs": {}, "methods": [ @@ -236,6 +232,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -262,9 +263,6 @@ "create": [], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -743,5 +741,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/tuple_support/out/NestedTuples.arc56.json b/test_cases/tuple_support/out/NestedTuples.arc56.json index c64a1665f6..e17506d344 100644 --- a/test_cases/tuple_support/out/NestedTuples.arc56.json +++ b/test_cases/tuple_support/out/NestedTuples.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "NestedTuples", "structs": { "Child": [ @@ -119,6 +115,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -153,9 +154,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -213,5 +211,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/tuple_support/out/client_NestedTuples.py b/test_cases/tuple_support/out/client_NestedTuples.py index 13e87db8ae..966fcffedf 100644 --- a/test_cases/tuple_support/out/client_NestedTuples.py +++ b/test_cases/tuple_support/out/client_NestedTuples.py @@ -9,10 +9,11 @@ class Child(algopy.arc4.Struct): a: algopy.arc4.UIntN[typing.Literal[64]] b: algopy.arc4.DynamicBytes c: algopy.arc4.String + class Parent(algopy.arc4.Struct): foo: algopy.arc4.UIntN[typing.Literal[64]] foo_arc: algopy.arc4.UIntN[typing.Literal[64]] - child: algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.DynamicBytes, algopy.arc4.String] + child: Child class NestedTuples(algopy.arc4.ARC4Client, typing.Protocol): @algopy.arc4.abimethod @@ -30,10 +31,10 @@ def nested_tuple_params( def named_tuple( self, args: Child, - ) -> algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.DynamicBytes, algopy.arc4.String]: ... + ) -> Child: ... @algopy.arc4.abimethod def nested_named_tuple_params( self, args: Parent, - ) -> algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.DynamicBytes, algopy.arc4.String]]: ... + ) -> Parent: ... diff --git a/test_cases/typed_abi_call/out/Greeter.arc56.json b/test_cases/typed_abi_call/out/Greeter.arc56.json index 6b830505c5..c0d4d2f188 100644 --- a/test_cases/typed_abi_call/out/Greeter.arc56.json +++ b/test_cases/typed_abi_call/out/Greeter.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Greeter", "structs": {}, "methods": [ @@ -355,6 +351,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -383,9 +384,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -530,5 +528,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/typed_abi_call/out/Logger.arc56.json b/test_cases/typed_abi_call/out/Logger.arc56.json index c66bd6b39d..c04c6dccf9 100644 --- a/test_cases/typed_abi_call/out/Logger.arc56.json +++ b/test_cases/typed_abi_call/out/Logger.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Logger", "structs": { "LogMessage": [ @@ -80,7 +76,7 @@ "recommendations": {} }, { - "name": "log_uint64", + "name": "log", "args": [ { "type": "uint64", @@ -101,7 +97,7 @@ "recommendations": {} }, { - "name": "log_uint512", + "name": "log", "args": [ { "type": "uint512", @@ -122,7 +118,7 @@ "recommendations": {} }, { - "name": "log_string", + "name": "log", "args": [ { "type": "string", @@ -143,7 +139,7 @@ "recommendations": {} }, { - "name": "log_bool", + "name": "log", "args": [ { "type": "bool", @@ -164,7 +160,7 @@ "recommendations": {} }, { - "name": "log_bytes", + "name": "log", "args": [ { "type": "byte[]", @@ -185,7 +181,7 @@ "recommendations": {} }, { - "name": "log_asset_account_app", + "name": "log", "args": [ { "type": "asset", @@ -476,6 +472,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -504,9 +505,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -614,5 +612,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/typed_abi_call_txn/out/Caller.arc56.json b/test_cases/typed_abi_call_txn/out/Caller.arc56.json index 0f60e112a7..042b8e5183 100644 --- a/test_cases/typed_abi_call_txn/out/Caller.arc56.json +++ b/test_cases/typed_abi_call_txn/out/Caller.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Caller", "structs": {}, "methods": [ @@ -123,6 +119,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -151,9 +152,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -224,5 +222,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/typed_abi_call_txn/out/TxnContract.arc56.json b/test_cases/typed_abi_call_txn/out/TxnContract.arc56.json index 93fa630098..c9be9d4862 100644 --- a/test_cases/typed_abi_call_txn/out/TxnContract.arc56.json +++ b/test_cases/typed_abi_call_txn/out/TxnContract.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "TxnContract", "structs": {}, "methods": [ @@ -94,6 +90,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -122,9 +123,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -210,5 +208,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/test_cases/unassigned_expression/out/Unassigned.arc56.json b/test_cases/unassigned_expression/out/Unassigned.arc56.json index 77a358876e..8f5763bfd0 100644 --- a/test_cases/unassigned_expression/out/Unassigned.arc56.json +++ b/test_cases/unassigned_expression/out/Unassigned.arc56.json @@ -1,8 +1,4 @@ { - "arcs": [ - 22, - 28 - ], "name": "Unassigned", "structs": {}, "methods": [ @@ -55,6 +51,11 @@ "recommendations": {} } ], + "arcs": [ + 22, + 28 + ], + "networks": {}, "state": { "schema": { "global": { @@ -83,9 +84,6 @@ ], "call": [] }, - "events": [], - "templateVariables": {}, - "networks": {}, "sourceInfo": { "approval": { "sourceInfo": [ @@ -134,5 +132,7 @@ "minor": 99, "patch": 99 } - } + }, + "events": [], + "templateVariables": {} } \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index 1768620046..b66c708dd2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -45,6 +45,7 @@ def run_puyapy( def run_puyapy_clientgen( path: Path, + *flags: str, ) -> subprocess.CompletedProcess[str]: puyapy_clientgen = shutil.which("puyapy-clientgen") assert puyapy_clientgen is not None @@ -52,6 +53,7 @@ def run_puyapy_clientgen( [ puyapy_clientgen, str(path), + *flags, ], text=True, # capture stdout @@ -100,13 +102,27 @@ def test_run_directory() -> None: run_puyapy([TEST_CASES_DIR / "simple"]) +@pytest.mark.parametrize( + "case", + [ + pytest.param(path, id=str(path.relative_to(VCS_ROOT))) + for test_dir in ("arc_56", "abi_routing") + for path in (TEST_CASES_DIR / test_dir).rglob("out/*.arc32.json") + ], +) +def test_puyapy_clientgen_arc32(case: Path, tmpdir: Path) -> None: + # ARC-32 output differs slightly from ARC-56, so we are just checking it doesn't error + # and ignore the generated artifact + run_puyapy_clientgen(case, "--out-dir", str(tmpdir)) + + @pytest.mark.parametrize( "case", [ pytest.param(path, id=str(path.relative_to(VCS_ROOT))) for test_dir in (EXAMPLES_DIR, TEST_CASES_DIR) - for path in test_dir.rglob("out/*.arc32.json") + for path in test_dir.rglob("out/*.arc56.json") ], ) -def test_puyapy_clientgen(case: Path) -> None: +def test_puyapy_clientgen_arc56(case: Path) -> None: run_puyapy_clientgen(case) diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 50cbf75b10..69d61deaed 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -188,7 +188,9 @@ def compile_src_from_options(options: PuyaPyOptions) -> CompilationResult: filtered_teal = [t for t in teal if t.id in narrow_compilation_set] if options.output_client: - write_arc32_clients(narrow_compilation_set, filtered_teal) + write_arc32_clients( + options.template_vars_prefix, narrow_compilation_set, filtered_teal + ) return CompilationResult( module_awst=awst,