diff --git a/src/algokit_utils/applications/app_spec/arc56.py b/src/algokit_utils/applications/app_spec/arc56.py index 590a14fc..a5cc0bbc 100644 --- a/src/algokit_utils/applications/app_spec/arc56.py +++ b/src/algokit_utils/applications/app_spec/arc56.py @@ -455,7 +455,10 @@ def to_abi_method(self) -> AlgosdkMethod: @staticmethod def from_dict(data: dict[str, Any]) -> Method: - data["actions"] = Actions.from_dict(data["actions"]) + if data.get("actions"): + data["actions"] = Actions.from_dict(data["actions"]) + else: + data["actions"] = Actions(call=list(CallEnum), create=list(CreateEnum)) data["args"] = [MethodArg.from_dict(item) for item in data["args"]] data["returns"] = Returns.from_dict(data["returns"]) if data.get("events"): @@ -888,19 +891,38 @@ class Arc56Contract: """The optional template variable information""" @staticmethod - def from_dict(application_spec: dict) -> Arc56Contract: + def from_dict(application_spec: dict) -> Arc56Contract: # noqa: C901, PLR0912 """Create Arc56Contract from dictionary. :param application_spec: Dictionary containing contract specification :return: Arc56Contract instance """ data = _dict_keys_to_snake_case(application_spec) - data["bare_actions"] = BareActions.from_dict(data["bare_actions"]) + + if data.get("bare_actions"): + data["bare_actions"] = BareActions.from_dict(data["bare_actions"]) + else: + data["bare_actions"] = BareActions(call=list(CallEnum), create=list(CreateEnum)) + data["methods"] = [Method.from_dict(item) for item in data["methods"]] - data["state"] = State.from_dict(data["state"]) - data["structs"] = { - key: [StructField.from_dict(item) for item in value] for key, value in application_spec["structs"].items() - } + + if data.get("state"): + data["state"] = State.from_dict(data["state"]) + else: + data["state"] = State( + keys=Keys(box={}, global_state={}, local_state={}), + maps=Maps(box={}, global_state={}, local_state={}), + schema=Schema(global_state=Global(bytes=0, ints=0), local_state=Local(bytes=0, ints=0)), + ) + + if data.get("structs"): + data["structs"] = { + key: [StructField.from_dict(item) for item in value] + for key, value in application_spec["structs"].items() + } + else: + data["structs"] = {} + if data.get("byte_code"): data["byte_code"] = ByteCode.from_dict(data["byte_code"]) if data.get("compiler_info"): @@ -921,6 +943,9 @@ def from_dict(application_spec: dict) -> Arc56Contract: data["template_variables"] = { key: TemplateVariables.from_dict(value) for key, value in data["template_variables"].items() } + + data["arcs"] = data.get("arcs", [4]) + return Arc56Contract(**data) @staticmethod diff --git a/tests/applications/test_app_client.py b/tests/applications/test_app_client.py index bfd16757..71e599e3 100644 --- a/tests/applications/test_app_client.py +++ b/tests/applications/test_app_client.py @@ -765,6 +765,11 @@ def nested_struct_app_spec() -> Arc56Contract: return Arc56Contract.from_json(raw_json_spec.read_text()) +def test_nested_struct_from_arc4() -> None: + raw_json_spec = Path(__file__).parent.parent / "artifacts" / "nested_struct" / "nested_struct.arc4.json" + Arc56Contract.from_json(raw_json_spec.read_text()) + + def test_nested_structs_described_by_structure( algorand: AlgorandClient, funded_account: SigningAccount, nested_struct_app_spec: Arc56Contract ) -> None: diff --git a/tests/artifacts/nested_struct/nested_struct.arc4.json b/tests/artifacts/nested_struct/nested_struct.arc4.json new file mode 100644 index 00000000..bba84f86 --- /dev/null +++ b/tests/artifacts/nested_struct/nested_struct.arc4.json @@ -0,0 +1,41 @@ +{ + "name": "NestedStruct", + "desc": "", + "methods": [ + { + "name": "createApplication", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "setValue", + "args": [ + { + "name": "key", + "type": "uint64" + }, + { + "name": "value", + "type": "string" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "getValue", + "args": [ + { + "name": "key", + "type": "uint64" + } + ], + "returns": { + "type": "((string))" + } + } + ] +} \ No newline at end of file