diff --git a/pycardano/plutus.py b/pycardano/plutus.py index d515118d..adbd8a75 100644 --- a/pycardano/plutus.py +++ b/pycardano/plutus.py @@ -902,3 +902,65 @@ class Unit(PlutusData): """The default "Unit type" with a 0 constructor ID""" CONSTR_ID = 0 + + +def to_plutus_schema(cls: Type[Datum]) -> dict: + """ + Convert to a dictionary representing a json schema according to CIP 57 Plutus Blueprint + Reference of the core structure: + https://cips.cardano.org/cips/cip57/#corevocabulary + + Args: + **kwargs: Extra key word arguments to be passed to `json.dumps()` + + Returns: + dict: a dict representing the schema of this class. + """ + if hasattr(cls, "__origin__") and cls.__origin__ is list: + return { + "dataType": "list", + **( + {"items": to_plutus_schema(cls.__args__[0])} + if hasattr(cls, "__args__") + else {} + ), + } + elif hasattr(cls, "__origin__") and cls.__origin__ is dict: + return { + "dataType": "map", + **( + { + "keys": to_plutus_schema(cls.__args__[0]), + "values": to_plutus_schema(cls.__args__[1]), + } + if hasattr(cls, "__args__") + else {} + ), + } + elif hasattr(cls, "__origin__") and cls.__origin__ is Union: + return { + "anyOf": [to_plutus_schema(t) for t in cls.__args__] + if hasattr(cls, "__args__") + else [] + } + elif issubclass(cls, PlutusData): + fields = [] + for field_value in cls.__dataclass_fields__.values(): + if field_value.name == "CONSTR_ID": + continue + field_schema = to_plutus_schema(field_value.type) + field_schema["title"] = field_value.name + fields.append(field_schema) + return { + "dataType": "constructor", + "index": cls.CONSTR_ID, + "fields": fields, + } + elif issubclass(cls, bytes) or issubclass(cls, ByteString): + return {"dataType": "bytes"} + elif issubclass(cls, int): + return {"dataType": "integer"} + elif issubclass(cls, IndefiniteList) or issubclass(cls, list): + return {"dataType": "list"} + else: + return {} diff --git a/test/pycardano/test_plutus.py b/test/pycardano/test_plutus.py index f16df8fe..96e7df90 100644 --- a/test/pycardano/test_plutus.py +++ b/test/pycardano/test_plutus.py @@ -21,6 +21,7 @@ id_map, Datum, Unit, + to_plutus_schema, ) from pycardano.serialization import IndefiniteList, RawCBOR, ByteString @@ -446,3 +447,125 @@ class A(PlutusData): assert ( A_tmp.to_cbor_hex() == quote_hex ), "Long metadata bytestring is encoded incorrectly." + + +def test_to_plutus_schema_basic(): + @dataclass + class A(PlutusData): + CONSTR_ID = 0 + a: int + b: bytes + c: ByteString + d: List[int] + + @dataclass + class C(PlutusData): + x: RawPlutusData + y: RawCBOR + z: Datum + w: IndefiniteList + + @dataclass + class B(PlutusData): + a: int + c: A + d: Dict[bytes, C] + e: Union[A, C] + + schema = to_plutus_schema(B) + assert schema == { + "dataType": "constructor", + "index": 3809077817, + "fields": [ + {"dataType": "integer", "title": "a"}, + { + "dataType": "constructor", + "index": 0, + "fields": [ + {"dataType": "integer", "title": "a"}, + {"dataType": "bytes", "title": "b"}, + {"dataType": "bytes", "title": "c"}, + { + "dataType": "list", + "items": {"dataType": "integer"}, + "title": "d", + }, + ], + "title": "c", + }, + { + "dataType": "map", + "keys": {"dataType": "bytes"}, + "values": { + "dataType": "constructor", + "index": 892310804, + "fields": [ + {"title": "x"}, + {"title": "y"}, + { + "anyOf": [ + { + "dataType": "constructor", + "index": 3577940042, + "fields": [], + }, + {}, + {"dataType": "integer"}, + {"dataType": "bytes"}, + {"dataType": "list"}, + {}, + {}, + ], + "title": "z", + }, + {"dataType": "list", "title": "w"}, + ], + }, + "title": "d", + }, + { + "anyOf": [ + { + "dataType": "constructor", + "index": 0, + "fields": [ + {"dataType": "integer", "title": "a"}, + {"dataType": "bytes", "title": "b"}, + {"dataType": "bytes", "title": "c"}, + { + "dataType": "list", + "items": {"dataType": "integer"}, + "title": "d", + }, + ], + }, + { + "dataType": "constructor", + "index": 892310804, + "fields": [ + {"title": "x"}, + {"title": "y"}, + { + "anyOf": [ + { + "dataType": "constructor", + "index": 3577940042, + "fields": [], + }, + {}, + {"dataType": "integer"}, + {"dataType": "bytes"}, + {"dataType": "list"}, + {}, + {}, + ], + "title": "z", + }, + {"dataType": "list", "title": "w"}, + ], + }, + ], + "title": "e", + }, + ], + }