diff --git a/examples/common/utils.py b/examples/common/utils.py index 43b9c8a..daf1a4b 100644 --- a/examples/common/utils.py +++ b/examples/common/utils.py @@ -121,9 +121,7 @@ async def random_account_with_weights( result = await client.execute_transaction(tx) new_addresses = [ - e.value.address - for e in result.events - if isinstance(e.value, cadence.AccountCreatedEvent) + e.value.address for e in result.events if e.value.id == "flow.AccountCreated" ] return ( diff --git a/examples/events_examples.py b/examples/events_examples.py index 14557c3..f7c4d95 100644 --- a/examples/events_examples.py +++ b/examples/events_examples.py @@ -126,7 +126,7 @@ async def run(self, ctx: Config): e.value for e in result.events if isinstance(e.value, cadence.Event) ][0] - assert add_event.fields[2].as_type(cadence.Int).value == 7 + assert add_event.sum.as_type(cadence.Int).value == 7 self.log.info(f"event type: {result.events[0].type}") self.log.info(f"event value: {result.events[0].value}") diff --git a/examples/scripts_examples.py b/examples/scripts_examples.py index da02605..5d9f9cd 100644 --- a/examples/scripts_examples.py +++ b/examples/scripts_examples.py @@ -121,11 +121,11 @@ async def run(self, ctx: Config): script_result: cadence.Value = complex_script self.log.info( - f"Name: {script_result.as_type(cadence.Struct).fields[2].as_type(cadence.String).value}" + f"Name: {script_result.as_type(cadence.Struct).name.as_type(cadence.String).value}" ) self.log.info( - f"Address: {script_result.as_type(cadence.Struct).fields[1].as_type(cadence.Address).bytes.hex()}" + f"Address: {script_result.as_type(cadence.Struct).address.as_type(cadence.Address).bytes.hex()}" ) self.log.info( - f"Balance: {script_result.as_type(cadence.Struct).fields[0].as_type(cadence.UFix64).value}" + f"Balance: {script_result.as_type(cadence.Struct).balance.as_type(cadence.UFix64).value}" ) diff --git a/examples/user_message_examples.py b/examples/user_message_examples.py index 9270d82..20de7f2 100644 --- a/examples/user_message_examples.py +++ b/examples/user_message_examples.py @@ -1,14 +1,10 @@ +from examples.common import Example, Config +from examples.common.utils import random_account_with_weights from flow_py_sdk import ( - SignAlgo, - HashAlgo, - InMemorySigner, - InMemoryVerifier, flow_client, AccountKey, utils, ) -from examples.common.utils import random_account, random_account_with_weights -from examples.common import Example, Config # ------------------------------------------------------------------------- diff --git a/flow_py_sdk/cadence/__init__.py b/flow_py_sdk/cadence/__init__.py index f33cb63..567fca3 100644 --- a/flow_py_sdk/cadence/__init__.py +++ b/flow_py_sdk/cadence/__init__.py @@ -1,11 +1,5 @@ from .decode import cadence_object_hook from .encode import CadenceJsonEncoder, encode_arguments -from .events import ( - Event, - BaseEvent, - EventTypeRegistry, - EventType, -) from .types import ( Value, Void, @@ -35,28 +29,93 @@ UFix64, Array, Dictionary, - Link, Path, Capability, + Function, KeyValuePair, TypeValue, ) from .composite import ( - Type, - CompositeType, Struct, - StructType, Resource, - ResourceType, - ContractType, Contract, - Field, + Event, + Enum, + Composite, +) +from .kind import ( + Kind, +) +from .simple_kinds import ( + AnyKind, + AnyStructKind, + AnyResourceKind, + TypeKind, + VoidKind, + NeverKind, + BoolKind, + StringKind, + CharacterKind, + BytesKind, + AddressKind, + NumberKind, + SignedNumberKind, + IntegerKind, + SignedIntegerKind, + FixedPointKind, + SignedFixedPointKind, + IntKind, + Int8Kind, + Int16Kind, + Int32Kind, + Int64Kind, + Int128Kind, + Int256Kind, + UIntKind, + UInt8Kind, + UInt16Kind, + UInt32Kind, + UInt64Kind, + UInt128Kind, + UInt256Kind, + Word8Kind, + Word16Kind, + Word32Kind, + Word64Kind, + Fix64Kind, + UFix64Kind, + PathKind, + CapabilityPathKind, + StoragePathKind, + PublicPathKind, + PrivatePathKind, + AuthAccountKind, + PublicAccountKind, + AuthAccountKeysKind, + PublicAccountKeysKind, + AuthAccountContractsKind, + PublicAccountContractsKind, + DeployedContractKind, + AccountKeyKind, + BlockKind, ) -from .events import AccountCreatedEvent -from .location import ( - StringLocation, - FlowLocation, - AddressLocation, - Location, - ScriptLocation, +from .kinds import ( + OptionalKind, + VariableSizedArrayKind, + ConstantSizedArrayKind, + DictionaryKind, + ParameterKind, + FieldKind, + StructKind, + ResourceKind, + EventKind, + ContractKind, + StructInterfaceKind, + ResourceInterfaceKind, + ContractInterfaceKind, + FunctionKind, + ReferenceKind, + RestrictedKind, + CapabilityKind, + EnumKind, ) diff --git a/flow_py_sdk/cadence/composite.py b/flow_py_sdk/cadence/composite.py index 5f60953..51b46fc 100644 --- a/flow_py_sdk/cadence/composite.py +++ b/flow_py_sdk/cadence/composite.py @@ -1,251 +1,90 @@ from __future__ import annotations -from abc import ABC -from typing import Tuple, Type as pyType +from abc import ABCMeta +import flow_py_sdk.cadence.constants as c from flow_py_sdk.cadence.decode import decode, add_cadence_decoder -from flow_py_sdk.cadence.location import Location, decode_location from flow_py_sdk.cadence.value import Value -import flow_py_sdk.cadence.constants as c - - -class CompositeType(ABC): - def __init__( - self, - location: Location, - qualified_identifier: str, - fields: list[Field], - initializer: list[list[Parameter]] = None, - ): - self.location: Location = location - self.qualified_identifier: str = qualified_identifier - self.fields: list[Field] = fields - self.initializers: list[list[Parameter]] = initializer - - def id(self) -> str: - return self.location.type_id(self.qualified_identifier) - - -class StructType(CompositeType): - pass - - -class Struct(Value): - def __init__(self, fields: list[Value], struct_type: StructType) -> None: - super().__init__() - self.struct_type: StructType = struct_type - self.fields: list[Value] = fields - - def __str__(self): - return Composite.format_composite( - self.struct_type.id(), - self.struct_type.fields, - self.fields, - ) - - def encode_value(self) -> dict: - return Composite.encode_composite( - c.structTypeStr, - self.struct_type.id(), - self.struct_type.fields, - self.fields, - ) - - @classmethod - def decode(cls, value) -> Struct: - composite = Composite.decode(value[c.valueKey]) - - struct_type = StructType( - composite.location, - composite.qualified_identifier, - composite.field_types, - ) - return Struct(composite.field_values, struct_type) - - @classmethod - def type_str(cls) -> str: - return c.structTypeStr - - -class ResourceType(CompositeType): - pass - - -class Resource(Value): - def __init__(self, fields: list[Value], resource_type: ResourceType) -> None: +class Composite(Value, metaclass=ABCMeta): + def __init__(self, id_: str, field_pairs: list[(str, Value)]): super().__init__() - self.resource_type: ResourceType = resource_type - self.fields: list[Value] = fields - def __str__(self): - return Composite.format_composite( - self.resource_type.id(), - self.resource_type.fields, - self.fields, - ) - - def encode_value(self) -> dict: - return Composite.encode_composite( - c.resourceTypeStr, - self.resource_type.id(), - self.resource_type.fields, - self.fields, - ) + self.fields: dict[str, Value] = {f[0]: f[1] for f in field_pairs} + self.field_order = [f[0] for f in field_pairs] + self.id = id_ @classmethod - def decode(cls, value) -> Resource: - composite = Composite.decode(value[c.valueKey]) - - resource_type = ResourceType( - composite.location, - composite.qualified_identifier, - composite.field_types, - ) - - return Resource(composite.field_values, resource_type) - - @classmethod - def type_str(cls) -> str: - return c.resourceTypeStr - - -class ContractType(CompositeType): - pass - - -class Contract(Value): - def __init__(self, fields: list[Value], resource_type: ContractType) -> None: - super().__init__() - self.resource_type: ContractType = resource_type - self.fields: list[Value] = fields - - def __str__(self): - return Composite.format_composite( - self.resource_type.id(), - self.resource_type.fields, - self.fields, - ) - - def encode_value(self) -> dict: - return Composite.encode_composite( - c.contractTypeStr, - self.resource_type.id(), - self.resource_type.fields, - self.fields, - ) - - @classmethod - def decode(cls, value) -> Contract: - composite = Composite.decode(value[c.valueKey]) - - contract_type = ContractType( - composite.location, - composite.qualified_identifier, - composite.field_types, - ) - - return Contract(composite.field_values, contract_type) - - @classmethod - def type_str(cls) -> str: - return c.contractTypeStr - - -class Field(object): - def __init__(self, identifier: str, _type) -> None: - super().__init__() - self.identifier: str = identifier - # not used anywhere yet! not typed - self.type = _type - - -class Composite(object): - def __init__( - self, - location: Location, - qualified_identifier: str, - field_values: list[Value], - field_types: list[Field], - ) -> None: - super().__init__() - self.location: Location = location - self.qualified_identifier: str = qualified_identifier - self.field_values: list[Value] = field_values - self.field_types: list[Field] = field_types + def decode(cls, value) -> "Composite": + v = value[c.valueKey] + field_pairs = [(f[c.nameKey], decode(f[c.valueKey])) for f in v[c.fieldsKey]] + id_ = v[c.idKey] + return cls(id_, field_pairs) - @classmethod - def encode_composite( - cls, kind: str, _id: str, fields: list[Field], values: list[Value] - ) -> dict: + def encode_value(self): return { - c.typeKey: kind, c.valueKey: { - c.idKey: _id, + c.idKey: self.id, c.fieldsKey: [ - {c.nameKey: f.identifier, c.valueKey: v.encode()} - for f, v in zip(fields, values) + {c.nameKey: f, c.valueKey: self.fields[f].encode()} + for f in self.field_order ], - }, + } } - @classmethod - def decode(cls, value) -> "Composite": - type_id = value[c.idKey] - location, qualified_identifier = decode_location(type_id) - fields = value[c.fieldsKey] + def __str__(self): + return f"{self.type_str()}({','.join([f'{k}:{v}' for k, v in self.fields.items()])})" - field_values = [] - field_types = [] + def __setattr__(self, key, value): + if key != "fields" and key in self.fields: + self.fields[key] = value + else: + super().__setattr__(key, value) - for field in fields: - field_value, field_type = Composite._decode_composite_field(field) - field_values.append(field_value) - field_types.append(field_type) + def __getattr__(self, key): + if key != "fields" and key in self.fields: + return self.fields[key] + else: + super(Value, self).__getattribute__(key) - return Composite(location, qualified_identifier, field_values, field_types) +class Struct(Composite): @classmethod - def _decode_composite_field(cls, value) -> Tuple[Value, Field]: - name = value[c.nameKey] - field_value = decode(value[c.valueKey]) - - field = Field(name, type(field_value)) + def type_str(cls) -> str: + return "Struct" - return field_value, field +class Resource(Composite): @classmethod - def format_composite( - cls, type_id: str, fields: list[Field], values: list[Value] - ) -> str: - prepared_fields: list[Tuple[str, str]] = [] - for v, f in zip(values, fields): - prepared_fields.append((f.identifier, str(v))) + def type_str(cls) -> str: + return "Resource" - return f"{type_id}({str.join(', ', [f'{n}: {v}' for n, v in prepared_fields])})" +class Event(Composite): @classmethod def type_str(cls) -> str: - return c.eventTypeStr + return "Event" -class Type(object): - pass +class Contract(Composite): + @classmethod + def type_str(cls) -> str: + return "Contract" -class Parameter(object): - def __init__(self) -> None: - super().__init__() - self.label: str - self.identifier: str - self.type: Type +class Enum(Composite): + @classmethod + def type_str(cls) -> str: + return "Enum" -cadence_types: list[pyType[Value]] = [ +cadence_types = [ Struct, Resource, + Event, Contract, + Enum, ] for t in cadence_types: diff --git a/flow_py_sdk/cadence/constants.py b/flow_py_sdk/cadence/constants.py index 6273804..ee0c7a4 100644 --- a/flow_py_sdk/cadence/constants.py +++ b/flow_py_sdk/cadence/constants.py @@ -1,5 +1,8 @@ typeKey = "type" +typeIdKey = "typeID" +kindKey = "kind" valueKey = "value" +sizeKey = "size" keyKey = "key" nameKey = "name" fieldsKey = "fields" @@ -11,6 +14,13 @@ staticTypeKey = "staticType" addressKey = "address" pathKey = "path" +initializersKey = "initializers" +labelKey = "label" +parametersKey = "parameters" +returnKey = "return" +authorizedKey = "authorized" +restrictionsKey = "restrictions" +functionTypeKey = "functionType" voidTypeStr = "Void" optionalTypeStr = "Optional" @@ -43,10 +53,12 @@ resourceTypeStr = "Resource" eventTypeStr = "Event" contractTypeStr = "Contract" +enumTypeStr = "Enum" linkTypeStr = "Link" pathTypeStr = "Path" typeTypeStr = "Type" capabilityTypeStr = "Capability" +functionTypeStr = "Function" fix64_scale = int(8) fix64_factor = int(100_000_000) diff --git a/flow_py_sdk/cadence/decode.py b/flow_py_sdk/cadence/decode.py index 10cf692..2725348 100644 --- a/flow_py_sdk/cadence/decode.py +++ b/flow_py_sdk/cadence/decode.py @@ -1,28 +1,46 @@ from typing import Any, Callable, Type from flow_py_sdk.cadence.value import Value +from flow_py_sdk.cadence.kind import Kind import flow_py_sdk.cadence.constants as c _cadence_decoders: dict[str, Callable[[Any], Value]] = {} +_cadence_kind_decoders: dict[str, Callable[[Any], Kind]] = {} def add_cadence_decoder(t: Type[Value]): _cadence_decoders[t.type_str()] = t.decode -def decode(obj: [dict[Any, Any]]) -> Value: - # json decoder starts from bottom up, so its possible that this is already decoded - if isinstance(obj, Value): +def add_cadence_kind_decoder(t: Type[Kind]): + _cadence_kind_decoders[t.kind_str()] = t.decode + + +def decode(obj: [dict[Any, Any]]) -> Value | Kind: + # json decoder starts from bottom up, so it's possible that this is already decoded + if isinstance(obj, Value) or isinstance(obj, Kind): return obj + + # if there is an id key, it's already decoded and it is either a field or a parameter + if c.idKey in obj: + return obj + # if there is no type key we cant decode it directly, but it could be part of a dictionary or composite or path - if c.typeKey not in obj: + if c.kindKey not in obj and c.typeKey not in obj: return obj - type_ = obj[c.typeKey] - if type_ in _cadence_decoders: - decoder = _cadence_decoders[type_] - return decoder(obj) + if c.kindKey in obj: + kind = obj[c.kindKey] + if kind in _cadence_kind_decoders: + decoder = _cadence_kind_decoders[kind] + return decoder(obj) + + if c.typeKey in obj: + type_ = obj[c.typeKey] + if type_ in _cadence_decoders: + decoder = _cadence_decoders[type_] + return decoder(obj) raise NotImplementedError() diff --git a/flow_py_sdk/cadence/encode.py b/flow_py_sdk/cadence/encode.py index 5c0991a..dd436d9 100644 --- a/flow_py_sdk/cadence/encode.py +++ b/flow_py_sdk/cadence/encode.py @@ -1,6 +1,7 @@ import json from typing import Any, Optional as Optional, Tuple, Callable +from flow_py_sdk.cadence.kind import Kind from flow_py_sdk.cadence.types import Value @@ -31,6 +32,8 @@ def __init__( def default(self, o: Any) -> Any: if isinstance(o, Value): return o.encode() + if isinstance(o, Kind): + return o.encode() return super().default(o) @@ -38,7 +41,7 @@ def encode_arguments(arguments: list[Value]) -> list[bytes]: if arguments is None: return [] # the separators and the new line are there to get an identical json as the flow-go-sdk does (usually). - # It doesnt need to be identical but it is convenient for comparative testing with the go-sdk. + # It doesn't need to be identical, but it is convenient for comparative testing with the go-sdk. return [ ( json.dumps( diff --git a/flow_py_sdk/cadence/events.py b/flow_py_sdk/cadence/events.py deleted file mode 100644 index 015d62f..0000000 --- a/flow_py_sdk/cadence/events.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Type - -from flow_py_sdk.cadence.value import Value -from flow_py_sdk.cadence.address import Address -from flow_py_sdk.cadence.decode import add_cadence_decoder -from flow_py_sdk.cadence.composite import Composite, CompositeType - -import flow_py_sdk.cadence.constants as c - - -class EventType(CompositeType): - pass - - -class EventTypeRegistry(object): - event_types: dict[str, Type[BaseEvent]] = {} - - @classmethod - def register_event_type(cls, event_type: Type[BaseEvent]): - EventTypeRegistry.event_types[event_type.event_id_constraint()] = event_type - - -class BaseEvent(Value, ABC): - def __init__(self, fields: list[Value], event_type: EventType) -> None: - super().__init__() - self.event_type: EventType = event_type - self.fields: list[Value] = fields - - def __str__(self): - return Composite.format_composite( - self.event_type.id(), - self.event_type.fields, - self.fields, - ) - - def encode_value(self) -> dict: - return Composite.encode_composite( - c.eventTypeStr, - self.event_type.id(), - self.event_type.fields, - self.fields, - ) - - @classmethod - def type_str(cls) -> str: - return c.eventTypeStr - - @classmethod - def decode(cls, value) -> BaseEvent: - composite = Composite.decode(value[c.valueKey]) - - event_type = EventType( - composite.location, - composite.qualified_identifier, - composite.field_types, - ) - - if event_type.id() in EventTypeRegistry.event_types: - event_class = EventTypeRegistry.event_types[event_type.id()] - else: - event_class = Event - - event = event_class(composite.field_values, event_type) - - return event - - @classmethod - @abstractmethod - def event_id_constraint(cls) -> str: - pass - - -class Event(BaseEvent): - @classmethod - def event_id_constraint(cls) -> str: - return "" - - -add_cadence_decoder(Event) - - -class AccountCreatedEvent(BaseEvent): - def __init__(self, fields: list[Value], event_type: EventType) -> None: - super().__init__(fields, event_type) - self.address: Address = self.fields[0].as_type(Address) - - @classmethod - def event_id_constraint(cls) -> str: - return "flow.AccountCreated" - - -EventTypeRegistry.register_event_type(AccountCreatedEvent) diff --git a/flow_py_sdk/cadence/kind.py b/flow_py_sdk/cadence/kind.py new file mode 100644 index 0000000..a263c57 --- /dev/null +++ b/flow_py_sdk/cadence/kind.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TypeVar, Type, Optional + +import flow_py_sdk.cadence.constants as c +from flow_py_sdk.exceptions import CadenceIncorrectTypeError + + +class Kind(ABC, object): + def __init__(self) -> None: + super().__init__() + + def encode(self) -> dict: + return {c.kindKey: self.kind_str()} | self.encode_kind() + + def as_kind(self, t: Type[TValue]) -> Optional[TValue]: + if isinstance(self, t): + return self + raise CadenceIncorrectTypeError(f"Value {self} is not of kind {t}") + + @abstractmethod + def encode_kind(self) -> dict: + pass + + @classmethod + @abstractmethod + def decode(cls, value) -> "Kind": + pass + + @classmethod + @abstractmethod + def kind_str(cls) -> str: + pass + + def __eq__(self, other): + if isinstance(other, Kind): + return str(self) == str(other) + return NotImplemented + + def __hash__(self): + """Overrides the default implementation""" + return hash(str(self)) + + +TValue = TypeVar("TValue", bound=Kind) diff --git a/flow_py_sdk/cadence/kinds.py b/flow_py_sdk/cadence/kinds.py new file mode 100644 index 0000000..99f4343 --- /dev/null +++ b/flow_py_sdk/cadence/kinds.py @@ -0,0 +1,404 @@ +from abc import ABCMeta + +from flow_py_sdk.cadence import Kind +import flow_py_sdk.cadence.constants as c +from flow_py_sdk.cadence.decode import decode, add_cadence_kind_decoder + + +class OptionalKind(Kind): + def __init__(self, value: Kind) -> None: + super().__init__() + self.value = value + + def __str__(self): + return f"{self.value}?" + + def encode_kind(self) -> dict: + return {c.typeKey: self.value.encode()} + + @classmethod + def kind_str(cls) -> str: + return "Optional" + + @classmethod + def decode(cls, value) -> "Kind": + v = value[c.typeKey] + return OptionalKind(decode(v)) + + +class VariableSizedArrayKind(Kind): + def __init__(self, value: Kind) -> None: + super().__init__() + self.value = value + + def __str__(self): + return f"[{self.value}]" + + def encode_kind(self) -> dict: + return {c.typeKey: self.value.encode()} + + @classmethod + def kind_str(cls) -> str: + return "VariableSizedArray" + + @classmethod + def decode(cls, value) -> "Kind": + v = value[c.typeKey] + return VariableSizedArrayKind(decode(v)) + + +class ConstantSizedArrayKind(Kind): + def __init__(self, value: Kind, size: int) -> None: + super().__init__() + self.value = value + self.size = size + + def __str__(self): + return f"[{self.value};{self.size}]" + + def encode_kind(self) -> dict: + return { + c.typeKey: self.value.encode(), + c.sizeKey: self.size, + } + + @classmethod + def kind_str(cls) -> str: + return "ConstantSizedArray" + + @classmethod + def decode(cls, value) -> "Kind": + v = value[c.typeKey] + size = value[c.sizeKey] + return ConstantSizedArrayKind(decode(v), int(size)) + + +class DictionaryKind(Kind): + def __init__(self, key: Kind, value: Kind) -> None: + super().__init__() + self.key = key + self.value = value + + def __str__(self): + return f"{{{self.key};{self.value}}}" + + def encode_kind(self) -> dict: + return { + c.keyKey: self.key.encode(), + c.valueKey: self.value.encode(), + } + + @classmethod + def kind_str(cls) -> str: + return "Dictionary" + + @classmethod + def decode(cls, value) -> "Kind": + v = value[c.valueKey] + key = value[c.keyKey] + return DictionaryKind(decode(key), decode(v)) + + +class ParameterKind: + def __init__(self, label: str, id_: str, value: Kind) -> None: + super().__init__() + self.label = label + self.id = id_ + self.value = value + + def encode(self): + return { + c.labelKey: self.label, + c.idKey: self.id, + c.typeKey: self.value.encode(), + } + + @classmethod + def decode(cls, value) -> "ParameterKind": + label = str(value[c.labelKey]) + id_ = str(value[c.idKey]) + v = value[c.typeKey] + + return ParameterKind(label, id_, decode(v)) + + def __str__(self): + return f"{self.label} {self.id}: {self.value}" + + +class FieldKind: + def __init__(self, id_: str, value: Kind) -> None: + super().__init__() + self.id = id_ + self.value = value + + def encode(self): + return { + c.idKey: self.id, + c.typeKey: self.value.encode(), + } + + @classmethod + def decode(cls, value) -> "FieldKind": + id_ = str(value[c.idKey]) + v = value[c.typeKey] + + return FieldKind(id_, decode(v)) + + def __str__(self): + return f"{self.id}: {self.value}" + + +class CompositeKind(Kind, metaclass=ABCMeta): + def __init__( + self, + type_id: str, + initializers: list[list[ParameterKind]], + fields: list[FieldKind], + ) -> None: + super().__init__() + self.type_id = type_id + self.initializers = initializers + self.fields = fields + + @classmethod + def decode(cls, value) -> "Kind": + type_id = str(value[c.typeIdKey]) + initializers = value[c.initializersKey] + fields = value[c.fieldsKey] + return cls( + str(type_id), + [[ParameterKind.decode(j) for j in i] for i in initializers], + [FieldKind.decode(i) for i in fields], + ) + + def encode_kind(self) -> dict: + return { + c.typeKey: "", + c.typeIdKey: self.type_id, + c.initializersKey: [[i.encode() for i in j] for j in self.initializers], + c.fieldsKey: [i.encode() for i in self.fields], + } + + def __str__(self): + return f"{self.type_id}({', '.join([str(i) for i in self.fields])})" + + +class StructKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "Struct" + + +class ResourceKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "Resource" + + +class EventKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "Event" + + +class ContractKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "Contract" + + +class StructInterfaceKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "StructInterface" + + +class ResourceInterfaceKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "ResourceInterface" + + +class ContractInterfaceKind(CompositeKind): + @classmethod + def kind_str(cls) -> str: + return "ContractInterface" + + +class FunctionKind(Kind): + def __init__( + self, type_id: str, parameters: list[ParameterKind], return_: Kind + ) -> None: + super().__init__() + self.type_id = type_id + self.parameters = parameters + self.return_ = return_ + + @classmethod + def decode(cls, value) -> "Kind": + type_id = str(value[c.typeIdKey]) + parameters = value[c.parametersKey] + return_ = value[c.returnKey] + return FunctionKind( + type_id, + [ParameterKind.decode(i) for i in parameters], + decode(return_), + ) + + def encode_kind(self) -> dict: + return { + c.typeIdKey: self.type_id, + c.parametersKey: [i.encode() for i in self.parameters], + c.returnKey: self.return_.encode(), + } + + def __str__(self): + return f"(({', '.join([str(p) for p in self.parameters])}): {self.return_})" + + @classmethod + def kind_str(cls) -> str: + return "Function" + + +class ReferenceKind(Kind): + def __init__(self, authorized: bool, type_: Kind) -> None: + super().__init__() + self.authorized = authorized + self.type = type_ + + @classmethod + def decode(cls, value) -> "Kind": + authorized = bool(value[c.authorizedKey]) + type_ = value[c.typeKey] + return ReferenceKind( + authorized, + decode(type_), + ) + + def encode_kind(self) -> dict: + return { + c.authorizedKey: self.authorized, + c.typeKey: self.type.encode(), + } + + def __str__(self): + return f"&{'auth' if self.authorized else ''}{self.type}" + + @classmethod + def kind_str(cls) -> str: + return "Reference" + + +class RestrictedKind(Kind): + def __init__(self, type_id: str, type_: Kind, restrictions: list[Kind]) -> None: + super().__init__() + self.type_id = type_id + self.type = type_ + self.restrictions = restrictions + + @classmethod + def decode(cls, value) -> "Kind": + type_id = str(value[c.typeIdKey]) + type_ = value[c.typeKey] + restrictions = value[c.restrictionsKey] + return RestrictedKind( + type_id, + decode(type_), + [decode(i) for i in restrictions], + ) + + def encode_kind(self) -> dict: + return { + c.typeIdKey: self.type_id, + c.typeKey: self.type.encode(), + c.restrictionsKey: [i.encode() for i in self.restrictions], + } + + def __str__(self): + return f"{self.type}{{{', '.join([str(r) for r in self.restrictions])}}}" + + @classmethod + def kind_str(cls) -> str: + return "Restriction" + + +class CapabilityKind(Kind): + def __init__(self, type_: Kind) -> None: + super().__init__() + self.type = type_ + + @classmethod + def decode(cls, value) -> "Kind": + type_ = value[c.typeKey] + return CapabilityKind( + decode(type_), + ) + + def encode_kind(self) -> dict: + return { + c.typeKey: self.type.encode(), + } + + def __str__(self): + return f"Capability<{self.type}>" + + @classmethod + def kind_str(cls) -> str: + return "Capability" + + +class EnumKind(Kind): + def __init__(self, type_id: str, type_: Kind, fields: list[FieldKind]) -> None: + super().__init__() + self.type_id = type_id + self.fields = fields + self.type_ = type_ + + @classmethod + def decode(cls, value) -> "Kind": + type_id = str(value[c.typeIdKey]) + type_ = value[c.typeKey] + fields = value[c.fieldsKey] + return cls( + str(type_id), + decode(type_), + [FieldKind.decode(i) for i in fields], + ) + + def encode_kind(self) -> dict: + return { + c.typeKey: self.type_.encode(), + c.typeIdKey: self.type_id, + c.initializersKey: [], + c.fieldsKey: [i.encode() for i in self.fields], + } + + def __str__(self): + return f"{self.type_id}({', '.join([str(i) for i in self.fields])})" + + @classmethod + def kind_str(cls) -> str: + return "Enum" + + +cadence_kinds = [ + OptionalKind, + VariableSizedArrayKind, + ConstantSizedArrayKind, + DictionaryKind, + StructKind, + ResourceKind, + EventKind, + ContractKind, + StructInterfaceKind, + ResourceInterfaceKind, + ContractInterfaceKind, + FunctionKind, + ReferenceKind, + RestrictedKind, + CapabilityKind, + EnumKind, +] + +for t in cadence_kinds: + add_cadence_kind_decoder(t) diff --git a/flow_py_sdk/cadence/location.py b/flow_py_sdk/cadence/location.py deleted file mode 100644 index fdaec15..0000000 --- a/flow_py_sdk/cadence/location.py +++ /dev/null @@ -1,312 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Tuple, Annotated - -from flow_py_sdk.cadence.address import Address -from flow_py_sdk.exceptions import CadenceEncodingError - - -class Location(ABC): - """Location describes the origin of a Cadence script. - - This could be a file, a transaction, or a smart contract. - """ - - def __init__(self): - super(Location, self).__init__() - - @abstractmethod - def __str__(self) -> str: - pass - - @abstractmethod - def id(self) -> str: - """ - - Returns - ------- - returns the canonical ID for this import location - """ - pass - - @abstractmethod - def type_id(self, qualified_identifier: str) -> str: - """ - - Parameters - ---------- - qualified_identifier - - Returns - ------- - returns a type ID for the given qualified_identifier - """ - pass - - @abstractmethod - def qualified_identifier(self, type_id: str) -> str: - """ - - Parameters - ---------- - type_id - - Returns - ------- - returns the qualified identifier for the given type_id - """ - pass - - @classmethod - @abstractmethod - def decode( - cls, type_id: str - ) -> Tuple["Location", Annotated[str, "qualified identifier"]]: - pass - - @classmethod - @abstractmethod - def prefix(cls) -> str: - pass - - def __eq__(self, other): - if isinstance(other, Location): - return str(self) == str(other) - return NotImplemented - - def __hash__(self): - """Overrides the default implementation""" - return hash(str(self)) - - -class AddressLocation(Location): - def __init__(self, address: Address, name: str) -> None: - super().__init__() - self.address: Address = address - self.name: str = name - - def __str__(self) -> str: - if not self.name: - return str(self.address) - return f"{self.address}.{self.name}" - - def id(self) -> str: - if not self.name: - return f"{self.prefix()}.{self.address.hex()}" - return f"{self.prefix()}.{self.address.hex()}.{self.name}" - - def type_id(self, qualified_identifier: str) -> str: - return f"{self.prefix()}.{self.address.hex()}.{qualified_identifier}" - - def qualified_identifier(self, type_id: str) -> str: - pieces = type_id.split(".") - if len(pieces) < 3: - return "" - return pieces[2] - - @classmethod - def decode( - cls, type_id: str - ) -> Tuple["AddressLocation", Annotated[str, "qualified identifier"]]: - err_prefix = "Invalid address location type ID" - if not type_id: - raise CadenceEncodingError(f"{err_prefix}: missing prefix.") - - parts = type_id.split(".", 4) - - if len(parts) == 1: - raise CadenceEncodingError(f"{err_prefix}: missing location.") - elif len(parts) == 2: - raise CadenceEncodingError(f"{err_prefix}: missing qualified identifier.") - - if parts[0] != cls.prefix(): - raise CadenceEncodingError( - f"{err_prefix}: invalid prefix, expected {cls.prefix()} got {parts[0]}." - ) - - address = Address.from_hex(parts[1]) - - if len(parts) == 3: - name = parts[2] - qualified_identifier = name - else: - name = parts[2] - qualified_identifier = f"{name}.{parts[3]}" - - location = AddressLocation(address, name) - - return location, qualified_identifier - - @classmethod - def prefix(cls) -> str: - return "A" - - -class FlowLocation(Location): - def __init__(self) -> None: - super().__init__() - - def __str__(self) -> str: - return f"{self.prefix()}" - - def id(self) -> str: - return f"{self.prefix()}" - - def type_id(self, qualified_identifier: str) -> str: - return f"{self.prefix()}.{qualified_identifier}" - - def qualified_identifier(self, type_id: str) -> str: - pieces = type_id.split(".", 2) - if len(pieces) < 2: - return "" - return pieces[1] - - @classmethod - def decode( - cls, type_id: str - ) -> Tuple["FlowLocation", Annotated[str, "qualified identifier"]]: - err_prefix = "invalid Flow location type ID" - if not type_id: - raise CadenceEncodingError(f"{err_prefix}: missing prefix.") - - parts = type_id.split(".", 2) - - if len(parts) == 1: - raise CadenceEncodingError(f"{err_prefix}: missing qualified identifier.") - - if parts[0] != cls.prefix(): - raise CadenceEncodingError( - f"{err_prefix}: invalid prefix, expected {cls.prefix()} got {parts[0]}." - ) - qualified_identifier = parts[1] - - location = FlowLocation() - - return location, qualified_identifier - - @classmethod - def prefix(cls) -> str: - return "flow" - - -class StringLocation(Location): - def __init__( - self, - location: str, - ) -> None: - super().__init__() - self.location: str = location - - def __str__(self) -> str: - return f"{self.location}" - - def id(self) -> str: - return f"{self.prefix()}.{self.location}" - - def type_id(self, qualified_identifier: str) -> str: - return f"{self.prefix()}.{self.location}.{qualified_identifier}" - - def qualified_identifier(self, type_id: str) -> str: - pieces = type_id.split(".", 3) - if len(pieces) < 3: - return "" - return pieces[2] - - @classmethod - def decode( - cls, type_id: str - ) -> Tuple["StringLocation", Annotated[str, "qualified identifier"]]: - err_prefix = "invalid string location type ID" - if not type_id: - raise CadenceEncodingError(f"{err_prefix}: missing prefix.") - - parts = type_id.split(".", 3) - - if len(parts) == 1: - raise CadenceEncodingError(f"{err_prefix}: missing location.") - if len(parts) == 2: - raise CadenceEncodingError(f"{err_prefix}: missing qualified identifier.") - - if parts[0] != cls.prefix(): - raise CadenceEncodingError( - f"{err_prefix}: invalid prefix, expected {cls.prefix()} got {parts[0]}." - ) - qualified_identifier = parts[2] - - location = StringLocation(parts[1]) - - return location, qualified_identifier - - @classmethod - def prefix(cls) -> str: - return "S" - - -class ScriptLocation(Location): - def __init__( - self, - location: str, - ) -> None: - super().__init__() - self.location: str = location - - def __str__(self) -> str: - return f"{self.location}" - - def id(self) -> str: - return f"{self.prefix()}.{self.location}" - - def type_id(self, qualified_identifier: str) -> str: - return f"{self.prefix()}.{self.location}.{qualified_identifier}" - - def qualified_identifier(self, type_id: str) -> str: - pieces = type_id.split(".", 3) - if len(pieces) < 3: - return "" - return pieces[2] - - @classmethod - def decode( - cls, type_id: str - ) -> Tuple["ScriptLocation", Annotated[str, "qualified identifier"]]: - err_prefix = "invalid script location type ID" - if not type_id: - raise CadenceEncodingError(f"{err_prefix}: missing prefix.") - - parts = type_id.split(".", 3) - - if len(parts) == 1: - raise CadenceEncodingError(f"{err_prefix}: missing location.") - if len(parts) == 2: - raise CadenceEncodingError(f"{err_prefix}: missing qualified identifier.") - - if parts[0] != cls.prefix(): - raise CadenceEncodingError( - f"{err_prefix}: invalid prefix, expected {cls.prefix()} got {parts[0]}." - ) - qualified_identifier = parts[2] - - location = ScriptLocation(parts[1]) - - return location, qualified_identifier - - @classmethod - def prefix(cls) -> str: - return "s" - - -_location_types = [AddressLocation, FlowLocation, StringLocation, ScriptLocation] - - -def decode_location( - type_id: str, -) -> Tuple["AddressLocation", Annotated[str, "qualified identifier"]]: - parts = type_id.split(".") - if len(parts) < 1: - raise CadenceEncodingError("Invalid type ID: missing prefix.") - - prefix = parts[0] - for loc in _location_types: - if loc.prefix() == prefix: - return loc.decode(type_id) - - raise CadenceEncodingError(f"Invalid type ID: cannot decode prefix {prefix}") diff --git a/flow_py_sdk/cadence/simple_kinds.py b/flow_py_sdk/cadence/simple_kinds.py new file mode 100644 index 0000000..b41e024 --- /dev/null +++ b/flow_py_sdk/cadence/simple_kinds.py @@ -0,0 +1,387 @@ +from __future__ import annotations + +from abc import ABCMeta + +import flow_py_sdk.cadence.constants as c +from flow_py_sdk.cadence.decode import add_cadence_kind_decoder +from flow_py_sdk.cadence.kind import Kind + + +class SimpleKind(Kind, metaclass=ABCMeta): + def __init__(self) -> None: + super().__init__() + + def encode_kind(self) -> dict: + return {} + + def __str__(self): + return self.kind_str() + + @classmethod + def decode(cls, value) -> "Kind": + v = value[c.kindKey] + return cls() + + +class AnyKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Any" + + +class AnyStructKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AnyStruct" + + +class AnyResourceKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AnyResource" + + +class TypeKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Type" + + +class VoidKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Void" + + +class NeverKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Never" + + +class BoolKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Bool" + + +class StringKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "String" + + +class CharacterKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Character" + + +class BytesKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Bytes" + + +class AddressKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Address" + + +class NumberKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Number" + + +class SignedNumberKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "SignedNumber" + + +class IntegerKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Integer" + + +class SignedIntegerKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "SignedInteger" + + +class FixedPointKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "FixedPoint" + + +class SignedFixedPointKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "SignedFixedPoint" + + +class IntKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int" + + +class Int8Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int8" + + +class Int16Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int16" + + +class Int32Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int32" + + +class Int64Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int64" + + +class Int128Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int128" + + +class Int256Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Int256" + + +class UIntKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt" + + +class UInt8Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt8" + + +class UInt16Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt16" + + +class UInt32Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt32" + + +class UInt64Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt64" + + +class UInt128Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt128" + + +class UInt256Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UInt256" + + +class Word8Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Word8" + + +class Word16Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Word16" + + +class Word32Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Word32" + + +class Word64Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Word64" + + +class Fix64Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Fix64" + + +class UFix64Kind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "UFix64" + + +class PathKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Path" + + +class CapabilityPathKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "CapabilityPath" + + +class StoragePathKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "StoragePath" + + +class PublicPathKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "PublicPath" + + +class PrivatePathKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "PrivatePath" + + +class AuthAccountKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AuthAccount" + + +class PublicAccountKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "PublicAccount" + + +class AuthAccountKeysKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AuthAccount.Keys" + + +class PublicAccountKeysKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "PublicAccount.Keys" + + +class AuthAccountContractsKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AuthAccount.Contracts" + + +class PublicAccountContractsKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "PublicAccount.Contracts" + + +class DeployedContractKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "DeployedContract" + + +class AccountKeyKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "AccountKey" + + +class BlockKind(SimpleKind): + @classmethod + def kind_str(cls) -> str: + return "Block" + + +cadence_kinds = [ + AnyKind, + AnyStructKind, + AnyResourceKind, + TypeKind, + VoidKind, + NeverKind, + BoolKind, + StringKind, + CharacterKind, + BytesKind, + AddressKind, + NumberKind, + SignedNumberKind, + IntegerKind, + SignedIntegerKind, + FixedPointKind, + SignedFixedPointKind, + IntKind, + Int8Kind, + Int16Kind, + Int32Kind, + Int64Kind, + Int128Kind, + Int256Kind, + UIntKind, + UInt8Kind, + UInt16Kind, + UInt32Kind, + UInt64Kind, + UInt128Kind, + UInt256Kind, + Word8Kind, + Word16Kind, + Word32Kind, + Word64Kind, + Fix64Kind, + UFix64Kind, + PathKind, + CapabilityPathKind, + StoragePathKind, + PublicPathKind, + PrivatePathKind, + AuthAccountKind, + PublicAccountKind, + AuthAccountKeysKind, + PublicAccountKeysKind, + AuthAccountContractsKind, + PublicAccountContractsKind, + DeployedContractKind, + AccountKeyKind, + BlockKind, +] + +for t in cadence_kinds: + add_cadence_kind_decoder(t) diff --git a/flow_py_sdk/cadence/types.py b/flow_py_sdk/cadence/types.py index bf70277..3bd5fdc 100644 --- a/flow_py_sdk/cadence/types.py +++ b/flow_py_sdk/cadence/types.py @@ -8,6 +8,7 @@ ) import flow_py_sdk.cadence.constants as c +from flow_py_sdk.cadence.kind import Kind from flow_py_sdk.cadence.address import Address from flow_py_sdk.cadence.decode import decode, add_cadence_decoder from flow_py_sdk.cadence.value import Value @@ -569,36 +570,6 @@ def type_str(cls) -> str: return c.dictionaryTypeStr -class Link(Value): - def __init__(self, target_path: Path, borrow_type: str) -> None: - super().__init__() - self.target_path: Path = target_path - self.borrow_type: str = borrow_type - - def __str__(self): - return f"Link<{self.borrow_type}>({self.target_path})" - - def encode_value(self) -> dict: - return { - c.valueKey: { - c.targetPathKey: self.target_path.encode(), - c.borrowTypeKey: self.borrow_type, - } - } - - @classmethod - def decode(cls, value) -> Link: - v = value[c.valueKey] - return Link( - decode(v[c.targetPathKey]).as_type(Path), - v[c.borrowTypeKey], - ) - - @classmethod - def type_str(cls) -> str: - return c.linkTypeStr - - class Path(Value): def __init__(self, domain: str, identifier: str) -> None: super().__init__() @@ -627,17 +598,17 @@ def type_str(cls) -> str: class TypeValue(Value): - def __init__(self, type_name: str = None) -> None: + def __init__(self, type_: Kind = None) -> None: super().__init__() - self.type_name: str = type_name + self.type_ = type_ def __str__(self): - return f"Type<{self.type_name}>()" + return f"Type<{str(self.type_) if self.type_ else ''}>()" def encode_value(self) -> dict: return { c.valueKey: { - c.staticTypeKey: (self.type_name if self.type_name is not None else "") + c.staticTypeKey: self.type_.encode() if self.type_ else "", } } @@ -645,7 +616,7 @@ def encode_value(self) -> dict: def decode(cls, value) -> TypeValue: v = value[c.valueKey] return TypeValue( - v[c.staticTypeKey] if v[c.staticTypeKey] != "" else None, + decode(v[c.staticTypeKey]) if v[c.staticTypeKey] else None, ) @classmethod @@ -654,18 +625,14 @@ def type_str(cls) -> str: class Capability(Value): - def __init__(self, path: Path, address: Address, borrow_type: str) -> None: + def __init__(self, path: Path, address: Address, borrow_type: Kind) -> None: super().__init__() self.address = address self.path = path self.borrow_type = borrow_type def __str__(self): - type_arg = ( - "" - if self.borrow_type is None or self.borrow_type == "" - else f"<{self.borrow_type}>" - ) + type_arg = "" if self.borrow_type is None else f"<{self.borrow_type}>" return f"Capability{type_arg}(address: {self.address}, path: {self.path})" def encode_value(self) -> dict: @@ -673,9 +640,7 @@ def encode_value(self) -> dict: c.valueKey: { c.pathKey: self.path.encode(), c.addressKey: self.address.encode_value()[c.valueKey], - c.borrowTypeKey: self.borrow_type - if self.borrow_type is not None - else "", + c.borrowTypeKey: self.borrow_type.encode(), } } @@ -684,13 +649,39 @@ def decode(cls, value) -> Capability: v = value[c.valueKey] path = decode(v[c.pathKey]).as_type(Path) address = Address.decode({c.valueKey: v[c.addressKey]}) - return Capability(path, address, v[c.borrowTypeKey]) + return Capability(path, address, decode(v[c.borrowTypeKey])) @classmethod def type_str(cls) -> str: return c.capabilityTypeStr +class Function(Value): + def __init__(self, function_type: Kind) -> None: + super().__init__() + self.function_type = function_type + + def __str__(self): + return f"Function{self.function_type}" + + def encode_value(self) -> dict: + return { + c.valueKey: { + c.functionTypeKey: self.function_type.encode(), + } + } + + @classmethod + def decode(cls, value) -> Function: + v = value[c.valueKey] + type_ = v[c.functionTypeKey] + return Function(decode(type_)) + + @classmethod + def type_str(cls) -> str: + return c.functionTypeStr + + cadence_types: list[pyType[Value]] = [ Void, Optional, @@ -720,9 +711,9 @@ def type_str(cls) -> str: Array, Dictionary, TypeValue, - Link, Path, Capability, + Function, ] for t in cadence_types: diff --git a/tests/cadence/encode_test.py b/tests/cadence/encode_test.py index 21b43db..6e0eee5 100644 --- a/tests/cadence/encode_test.py +++ b/tests/cadence/encode_test.py @@ -1,14 +1,13 @@ import json import unittest from dataclasses import dataclass - from flow_py_sdk import cadence @dataclass class _EncodeTestParams: name: str - val: cadence.Value + val: cadence.Value | cadence.Kind expected: str @@ -33,17 +32,13 @@ def _encode(self, val: cadence.Value, expected_json: str) -> str: cls=cadence.CadenceJsonEncoder, separators=(",", ":"), ) - expected = json.loads(expected_json) actual = json.loads(actual_json) - self.assertDictEqual(expected, actual) - return actual_json def _decode(self, actual_json: str, expected_val: cadence.Value): cadence_val = json.loads(actual_json, object_hook=cadence.cadence_object_hook) - self.assertEqual(expected_val, cadence_val) def testEncodeOptional(self): @@ -510,7 +505,6 @@ def testEncodeArray(self): cadence.Array([]), '{"type":"Array","value":[]}', ) - int_array = _EncodeTestParams( "Integers", cadence.Array( @@ -523,14 +517,13 @@ def testEncodeArray(self): """{"type":"Array", "value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]}""", ) - resource_array = _EncodeTestParams( "Resources", cadence.Array( [ - cadence.Resource([cadence.Int(1)], _foo_resource_type), - cadence.Resource([cadence.Int(2)], _foo_resource_type), - cadence.Resource([cadence.Int(3)], _foo_resource_type), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(1))]), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(2))]), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(3))]), ] ), """{"type":"Array","value":[ @@ -542,7 +535,6 @@ def testEncodeArray(self): "fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}} ]}""", ) - self._encodeAndDecodeAll( [ empty_array, @@ -576,7 +568,6 @@ def testEncodeDictionary(self): {"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}} ]}""", ) - nested_dict = _EncodeTestParams( "Nested", cadence.Dictionary( @@ -660,22 +651,21 @@ def testEncodeDictionary(self): } """, ) - resource_dict = _EncodeTestParams( "Resources", cadence.Dictionary( [ cadence.KeyValuePair( cadence.String("a"), - cadence.Resource([cadence.Int(1)], _foo_resource_type), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(1))]), ), cadence.KeyValuePair( cadence.String("b"), - cadence.Resource([cadence.Int(2)], _foo_resource_type), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(2))]), ), cadence.KeyValuePair( cadence.String("c"), - cadence.Resource([cadence.Int(3)], _foo_resource_type), + cadence.Resource("S.test.Foo", [("bar", cadence.Int(3))]), ), ] ), @@ -723,7 +713,6 @@ def testEncodeDictionary(self): } """, ) - self._encodeAndDecodeAll( [ simple_dict, @@ -733,24 +722,15 @@ def testEncodeDictionary(self): ) def testEncodeResource(self): - foo_resource_type = cadence.ResourceType( - _test_location, - "Foo", - [ - cadence.Field( - "uuid", - type(cadence.UInt64), - ), - cadence.Field( - "bar", - type(cadence.Int), - ), - ], - ) - simple_resource = _EncodeTestParams( "Simple", - cadence.Resource([cadence.UInt64(0), cadence.Int(42)], foo_resource_type), + cadence.Resource( + "S.test.Foo", + [ + ("uuid", cadence.UInt64(0)), + ("bar", cadence.Int(42)), + ], + ), """ { "type": "Resource", @@ -764,47 +744,20 @@ def testEncodeResource(self): } """, ) - - bar_resource_type = cadence.ResourceType( - _test_location, - "Bar", - [ - cadence.Field( - "uuid", - type(cadence.UInt64), - ), - cadence.Field( - "x", - type(cadence.Int), - ), - ], - ) - - foo_resource_type = cadence.ResourceType( - _test_location, - "Foo", - [ - cadence.Field( - "uuid", - type(cadence.UInt64), - ), - cadence.Field( - "bar", - bar_resource_type, - ), - ], - ) - nested_resource = _EncodeTestParams( "Nested resource", cadence.Resource( + "S.test.Foo", [ - cadence.UInt64(0), - cadence.Resource( - [cadence.UInt64(0), cadence.Int(42)], bar_resource_type + ("uuid", cadence.UInt64(0)), + ( + "bar", + cadence.Resource( + "S.test.Bar", + [("uuid", cadence.UInt64(0)), ("x", cadence.Int(42))], + ), ), ], - foo_resource_type, ), """ { @@ -831,28 +784,15 @@ def testEncodeResource(self): } """, ) - self._encodeAndDecodeAll([simple_resource, nested_resource]) def testEncodeStruct(self): - simple_struct_type = cadence.StructType( - _test_location, - "FooStruct", - [ - cadence.Field( - "a", - type(cadence.Int), - ), - cadence.Field( - "b", - type(cadence.String), - ), - ], - ) - simple_struct = _EncodeTestParams( "Simple", - cadence.Struct([cadence.Int(1), cadence.String("foo")], simple_struct_type), + cadence.Struct( + "S.test.FooStruct", + [("a", cadence.Int(1)), ("b", cadence.String("foo"))], + ), """ { "type": "Struct", @@ -866,30 +806,14 @@ def testEncodeStruct(self): } """, ) - - resource_struct_type = cadence.StructType( - _test_location, - "FooStruct", - [ - cadence.Field( - "a", - type(cadence.String), - ), - cadence.Field( - "b", - _foo_resource_type, - ), - ], - ) - resource_struct = _EncodeTestParams( "Resources", cadence.Struct( + "S.test.FooStruct", [ - cadence.String("foo"), - cadence.Resource([cadence.Int(42)], _foo_resource_type), + ("a", cadence.String("foo")), + ("b", cadence.Resource("S.test.Foo", [("bar", cadence.Int(42))])), ], - resource_struct_type, ), """ { @@ -915,27 +839,14 @@ def testEncodeStruct(self): } """, ) - self._encodeAndDecodeAll([simple_struct, resource_struct]) def testEncodeEvent(self): - simple_event_type = cadence.EventType( - _test_location, - "FooEvent", - [ - cadence.Field( - "a", - type(cadence.Int), - ), - cadence.Field( - "b", - type(cadence.String), - ), - ], - ) simple_event = _EncodeTestParams( "Simple", - cadence.Event([cadence.Int(1), cadence.String("foo")], simple_event_type), + cadence.Event( + "S.test.FooEvent", [("a", cadence.Int(1)), ("b", cadence.String("foo"))] + ), """ { "type": "Event", @@ -949,30 +860,14 @@ def testEncodeEvent(self): } """, ) - - resource_event_type = cadence.EventType( - _test_location, - "FooEvent", - [ - cadence.Field( - "a", - type(cadence.Int), - ), - cadence.Field( - "b", - _foo_resource_type, - ), - ], - ) - resource_event = _EncodeTestParams( "Resources", cadence.Event( + "S.test.FooEvent", [ - cadence.String("foo"), - cadence.Resource([cadence.Int(42)], _foo_resource_type), + ("a", cadence.String("foo")), + ("b", cadence.Resource("S.test.Foo", [("bar", cadence.Int(42))])), ], - resource_event_type, ), """ { @@ -998,28 +893,17 @@ def testEncodeEvent(self): } """, ) - self._encodeAndDecodeAll([simple_event, resource_event]) def testEncodeContract(self): - simple_contract_type = cadence.ContractType( - _test_location, - "FooContract", - [ - cadence.Field( - "a", - type(cadence.Int), - ), - cadence.Field( - "b", - type(cadence.String), - ), - ], - ) simple_contract = _EncodeTestParams( "Simple", cadence.Contract( - [cadence.Int(1), cadence.String("foo")], simple_contract_type + "S.test.FooContract", + [ + ("a", cadence.Int(1)), + ("b", cadence.String("foo")), + ], ), """ { @@ -1034,30 +918,14 @@ def testEncodeContract(self): } """, ) - - resource_contract_type = cadence.ContractType( - _test_location, - "FooContract", - [ - cadence.Field( - "a", - type(cadence.Int), - ), - cadence.Field( - "b", - _foo_resource_type, - ), - ], - ) - resource_contract = _EncodeTestParams( "Resources", cadence.Contract( + "S.test.FooContract", [ - cadence.String("foo"), - cadence.Resource([cadence.Int(42)], _foo_resource_type), + ("a", cadence.String("foo")), + ("b", cadence.Resource("S.test.Foo", [("bar", cadence.Int(42))])), ], - resource_contract_type, ), """ { @@ -1083,55 +951,34 @@ def testEncodeContract(self): } """, ) - self._encodeAndDecodeAll([simple_contract, resource_contract]) - def testEncodeLink(self): - link = _EncodeTestParams( - "Link", - cadence.Link( - cadence.Path("storage", "foo"), - "Bar", + def testEncodeType(self): + static_type = _EncodeTestParams( + "Static Type", + cadence.TypeValue( + cadence.IntKind(), ), """ { - "type": "Link", + "type": "Type", "value": { - "targetPath": { - "type": "Path", - "value": { "domain": "storage", "identifier": "foo" } - }, - "borrowType": "Bar" + "staticType": { + "kind": "Int" + } } } """, ) - - self._encodeAndDecodeAll([link]) - - def testEncodeType(self): - static_type = _EncodeTestParams( - "Static Type", - cadence.TypeValue( - "Int", - ), - """{"type":"Type","value":{"staticType":"Int"}}""", - ) - no_static_type = _EncodeTestParams( - "No Static Type", - cadence.TypeValue(), - """{"type":"Type","value":{"staticType":""}}""", - ) - - self._encodeAndDecodeAll([static_type, no_static_type]) + self._encodeAndDecodeAll([static_type]) def testEncodeCapability(self): capability = _EncodeTestParams( "Capability", cadence.Capability( - cadence.Path("storage", "foo"), - cadence.Address.from_hex("0x0000000102030405"), - "Int", + cadence.Path("public", "someInteger"), + cadence.Address.from_hex("0x0000000000000001"), + cadence.IntKind(), ), """ { @@ -1139,43 +986,559 @@ def testEncodeCapability(self): "value": { "path": { "type": "Path", - "value": { "domain": "storage", "identifier": "foo" } + "value": { + "domain": "public", + "identifier": "someInteger" + } }, - "borrowType": "Int", - "address": "0x0000000102030405" + "address": "0x0000000000000001", + "borrowType": { + "kind": "Int" + } } } """, ) + self._encodeAndDecodeAll([capability]) + def testEncodeFunction(self): + capability = _EncodeTestParams( + "Function", + cadence.Function( + cadence.FunctionKind( + "fun():Void", + [], + cadence.VoidKind(), + ), + ), + """ + { + "type": "Function", + "value": { + "functionType": { + "kind": "Function", + "typeID": "fun():Void", + "parameters": [], + "return": { + "kind": "Void" + } + } + } + } + """, + ) self._encodeAndDecodeAll([capability]) + def testEncodeSimpleKind(self): + simple_kind = _EncodeTestParams( + "Simple Kind", + cadence.UInt8Kind(), + """ + { + "kind": "UInt8" + } + """, + ) + self._encodeAndDecodeAll([simple_kind]) + + def testEncodeOptionalKind(self): + optional_kind = _EncodeTestParams( + "Optional Kind", + cadence.OptionalKind(cadence.StringKind()), + """ + { + "kind": "Optional", + "type": { + "kind": "String" + } + } + """, + ) + self._encodeAndDecodeAll([optional_kind]) + + def testVariableSizedArrayKind(self): + kind = _EncodeTestParams( + "VariableSizedArray Kind", + cadence.VariableSizedArrayKind(cadence.StringKind()), + """ + { + "kind": "VariableSizedArray", + "type": { + "kind": "String" + } + } + """, + ) + self._encodeAndDecodeAll([kind]) -# func TestEncodeCapability(t *testing.T) { -# -# t.Parallel() -# -# testEncodeAndDecode( -# t, -# cadence.Capability{ -# Path: cadence.Path{Domain: "storage", Identifier: "foo"}, -# Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), -# BorrowType: "Int", -# }, -# `{"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":"Int","address":"0x0000000102030405"}}`, -# ) -# } + def testConstSizedArrayKind(self): + kind = _EncodeTestParams( + "ConstantSizedArray Kind", + cadence.ConstantSizedArrayKind(cadence.StringKind(), 3), + """ + { + "kind": "ConstantSizedArray", + "type": { + "kind": "String" + }, + "size":3 + } + """, + ) + self._encodeAndDecodeAll([kind]) + + def testDictionaryKind(self): + kind = _EncodeTestParams( + "Dictionary Kind", + cadence.DictionaryKind(cadence.StringKind(), cadence.UInt16Kind()), + """ + { + "kind": "Dictionary", + "key": { + "kind": "String" + }, + "value": { + "kind": "UInt16" + } + } + """, + ) + self._encodeAndDecodeAll([kind]) + def testCompositeKind(self): + kind = _EncodeTestParams( + "Resource Kind", + cadence.ResourceKind( + "0x3.GreatContract.GreatNFT", + [ + [ + cadence.ParameterKind( + "foo", + "bar", + cadence.StringKind(), + ) + ] + ], + [ + cadence.FieldKind( + "foo", + cadence.StringKind(), + ), + ], + ), + """ + { + "kind": "Resource", + "type": "", + "typeID": "0x3.GreatContract.GreatNFT", + "initializers":[ + [ + { + "label": "foo", + "id": "bar", + "type": { + "kind": "String" + } + } + ] + ], + "fields": [ + { + "id": "foo", + "type": { + "kind": "String" + } + } + ] + } + """, + ) + self._encodeAndDecodeAll([kind]) -_test_location = cadence.StringLocation("test") + def testFunctionKind(self): + kind = _EncodeTestParams( + "Function Kind", + cadence.FunctionKind( + "foo", + [ + cadence.ParameterKind( + "foo", + "bar", + cadence.StringKind(), + ) + ], + cadence.StringKind(), + ), + """ + { + "kind": "Function", + "typeID": "foo", + "parameters": [ + { + "label": "foo", + "id": "bar", + "type": { + "kind": "String" + } + } + ], + "return": { + "kind": "String" + } + } + """, + ) + self._encodeAndDecodeAll([kind]) + + def testReferenceKind(self): + kind = _EncodeTestParams( + "Reference Kind", + cadence.ReferenceKind( + True, + cadence.StringKind(), + ), + """ + { + "kind": "Reference", + "authorized": true, + "type": { + "kind": "String" + } + } + """, + ) + self._encodeAndDecodeAll([kind]) + + def testRestrictedKind(self): + kind = _EncodeTestParams( + "Restricted Kind", + cadence.RestrictedKind( + "0x3.GreatContract.GreatNFT", + cadence.AnyResourceKind(), + [ + cadence.ResourceInterfaceKind( + "0x1.FungibleToken.Receiver", + [], + [ + cadence.FieldKind( + "uuid", + cadence.UInt64Kind(), + ) + ], + ) + ], + ), + """ + { + "kind": "Restriction", + "typeID": "0x3.GreatContract.GreatNFT", + "type": { + "kind": "AnyResource" + }, + "restrictions": [ + { + "kind": "ResourceInterface", + "typeID": "0x1.FungibleToken.Receiver", + "initializers": [], + "fields": [ + { + "id": "uuid", + "type": { + "kind": "UInt64" + } + } + ], + "type": "" + } + ] + } + """, + ) + self._encodeAndDecodeAll([kind]) + + def testCapabilityKind(self): + kind = _EncodeTestParams( + "Capability Kind", + cadence.CapabilityKind( + cadence.ReferenceKind( + True, + cadence.StringKind(), + ) + ), + """ + { + "kind": "Capability", + "type": { + "kind": "Reference", + "authorized": true, + "type": { + "kind": "String" + } + } + } + """, + ) + self._encodeAndDecodeAll([kind]) + + def testEnumKind(self): + kind = _EncodeTestParams( + "Enum Kind", + cadence.EnumKind( + "0x3.GreatContract.GreatEnum", + cadence.StringKind(), + [ + cadence.FieldKind( + "rawValue", + cadence.StringKind(), + ) + ], + ), + """ + { + "kind": "Enum", + "type": { + "kind": "String" + }, + "typeID": "0x3.GreatContract.GreatEnum", + "initializers":[], + "fields": [ + { + "id": "rawValue", + "type": { + "kind": "String" + } + } + ] + } + """, + ) + self._encodeAndDecodeAll([kind]) -_foo_resource_type = cadence.ResourceType( - _test_location, - "Foo", - [ - cadence.Field( - "bar", - type(cadence.Int), - ), - ], -) + def testStorefrontEvent(self): + self.maxDiff = None + event = _EncodeTestParams( + "StorefrontEvent", + cadence.Event( + "A.4eb8a10cb9f87357.NFTStorefrontV2.ListingAvailable", + [ + ( + "storefrontAddress", + cadence.Address.from_hex("0xe037c7e7cd998a9c"), + ), + ("listingResourceID", cadence.UInt64(897392501)), + ( + "nftType", + cadence.TypeValue( + cadence.ResourceKind( + "A.87ca73a41bb50ad5.Golazos.NFT", + [], + [ + cadence.FieldKind("uuid", cadence.UInt64Kind()), + cadence.FieldKind("id", cadence.UInt64Kind()), + cadence.FieldKind( + "editionID", cadence.UInt64Kind() + ), + cadence.FieldKind( + "serialNumber", cadence.UInt64Kind() + ), + cadence.FieldKind( + "mintingDate", cadence.UFix64Kind() + ), + ], + ) + ), + ), + ("nftUUID", cadence.UInt64(885861011)), + ("nftID", cadence.UInt64(885861011)), + ( + "salePaymentVaultType", + cadence.TypeValue( + cadence.ResourceKind( + "A.ead892083b3e2c6c.DapperUtilityCoin.Vault", + [], + [ + cadence.FieldKind("uuid", cadence.UInt64Kind()), + cadence.FieldKind("balance", cadence.UFix64Kind()), + ], + ) + ), + ), + ("salePrice", cadence.UFix64(200000000)), + ( + "customID", + cadence.Optional(cadence.String("DAPPER_MARKETPLACE")), + ), + ("commissionAmount", cadence.UFix64(0)), + ( + "commissionReceivers", + cadence.Optional( + cadence.Array( + [ + cadence.Address.from_hex("0x87ca73a41bb50ad5"), + ] + ) + ), + ), + ("expiry", cadence.UInt64(33233716780)), + ], + ), + """ + { + "value": { + "id": "A.4eb8a10cb9f87357.NFTStorefrontV2.ListingAvailable", + "fields": [ + { + "value": { + "value": "0xe037c7e7cd998a9c", + "type": "Address" + }, + "name": "storefrontAddress" + }, + { + "value": { + "value": "897392501", + "type": "UInt64" + }, + "name": "listingResourceID" + }, + { + "value": { + "value": { + "staticType": { + "type": "", + "kind": "Resource", + "typeID": "A.87ca73a41bb50ad5.Golazos.NFT", + "fields": [ + { + "type": { + "kind": "UInt64" + }, + "id": "uuid" + }, + { + "type": { + "kind": "UInt64" + }, + "id": "id" + }, + { + "type": { + "kind": "UInt64" + }, + "id": "editionID" + }, + { + "type": { + "kind": "UInt64" + }, + "id": "serialNumber" + }, + { + "type": { + "kind": "UFix64" + }, + "id": "mintingDate" + } + ], + "initializers": [] + } + }, + "type": "Type" + }, + "name": "nftType" + }, + { + "value": { + "value": "885861011", + "type": "UInt64" + }, + "name": "nftUUID" + }, + { + "value": { + "value": "885861011", + "type": "UInt64" + }, + "name": "nftID" + }, + { + "value": { + "value": { + "staticType": { + "type": "", + "kind": "Resource", + "typeID": "A.ead892083b3e2c6c.DapperUtilityCoin.Vault", + "fields": [ + { + "type": { + "kind": "UInt64" + }, + "id": "uuid" + }, + { + "type": { + "kind": "UFix64" + }, + "id": "balance" + } + ], + "initializers": [] + } + }, + "type": "Type" + }, + "name": "salePaymentVaultType" + }, + { + "value": { + "value": "2.00000000", + "type": "UFix64" + }, + "name": "salePrice" + }, + { + "value": { + "value": { + "value": "DAPPER_MARKETPLACE", + "type": "String" + }, + "type": "Optional" + }, + "name": "customID" + }, + { + "value": { + "value": "0.00000000", + "type": "UFix64" + }, + "name": "commissionAmount" + }, + { + "value": { + "value": { + "value": [ + { + "value": "0x87ca73a41bb50ad5", + "type": "Address" + } + ], + "type": "Array" + }, + "type": "Optional" + }, + "name": "commissionReceivers" + }, + { + "value": { + "value": "33233716780", + "type": "UInt64" + }, + "name": "expiry" + } + ] + }, + "type": "Event" + } + """, + ) + self._encodeAndDecodeAll([event]) diff --git a/tests/cadence/location_test.py b/tests/cadence/location_test.py deleted file mode 100644 index 518e40d..0000000 --- a/tests/cadence/location_test.py +++ /dev/null @@ -1,163 +0,0 @@ -import unittest -from dataclasses import dataclass - -from flow_py_sdk import cadence -from flow_py_sdk.exceptions import CadenceEncodingError - - -class TestLocation(unittest.TestCase): - def testLocationId(self): - location_str = "0102" - type_id_str = "something" - - @dataclass - class TestCase: - location: cadence.Location - expected_id: str - expected_type_id: str - - def __init__(self, loc, expected_id, expected_type_id) -> None: - super().__init__() - self.location = loc - self.expected_id = expected_id - self.expected_type_id = expected_type_id - - cases: list[TestCase] = [ - TestCase( - cadence.ScriptLocation(location_str), - f"s.{location_str}", - f"s.{location_str}.{type_id_str}", - ), - TestCase( - cadence.AddressLocation(cadence.Address.from_hex("0x01"), location_str), - f"A.0000000000000001.{location_str}", - f"A.0000000000000001.{type_id_str}", - ), - TestCase( - cadence.StringLocation(location_str), - f"S.{location_str}", - f"S.{location_str}.{type_id_str}", - ), - TestCase(cadence.FlowLocation(), "flow", f"flow.{type_id_str}"), - ] - - for case in cases: - with self.subTest(msg=f"{case} id"): - location_id = case.location.id() - self.assertEqual(case.expected_id, location_id) - - with self.subTest(msg=f"{case} type_id"): - location_id = case.location.type_id(type_id_str) - self.assertEqual(case.expected_type_id, location_id) - - def testDecodeAddressLocation(self): - with self.subTest(msg=f"missing prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.AddressLocation.decode("") - - with self.subTest(msg=f"missing location"): - with self.assertRaises(CadenceEncodingError): - cadence.AddressLocation.decode("A") - - with self.subTest(msg=f"missing qualified identifier"): - with self.assertRaises(CadenceEncodingError): - cadence.AddressLocation.decode("A.0000000000000001") - - with self.subTest(msg=f"invalid prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.AddressLocation.decode("s.0000000000000001.t") - - with self.subTest(msg=f"decode"): - location, identifier = cadence.AddressLocation.decode( - "A.0000000000000001.test" - ) - self.assertEqual( - cadence.AddressLocation( - cadence.Address.from_hex("0x0000000000000001"), "test" - ), - location, - ) - self.assertEqual( - "test", - identifier, - ) - - def testDecodeScriptLocation(self): - with self.subTest(msg=f"missing prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.ScriptLocation.decode("") - - with self.subTest(msg=f"missing location"): - with self.assertRaises(CadenceEncodingError): - cadence.ScriptLocation.decode("s") - - with self.subTest(msg=f"missing qualified identifier"): - with self.assertRaises(CadenceEncodingError): - cadence.ScriptLocation.decode("s.test") - - with self.subTest(msg=f"invalid prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.ScriptLocation.decode("A.test.t") - - with self.subTest(msg=f"decode"): - location, identifier = cadence.ScriptLocation.decode("s.test.T") - self.assertEqual( - cadence.ScriptLocation("test"), - location, - ) - self.assertEqual( - "T", - identifier, - ) - - def testDecodeStringLocation(self): - with self.subTest(msg=f"missing prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.StringLocation.decode("") - - with self.subTest(msg=f"missing location"): - with self.assertRaises(CadenceEncodingError): - cadence.StringLocation.decode("S") - - with self.subTest(msg=f"missing qualified identifier"): - with self.assertRaises(CadenceEncodingError): - cadence.StringLocation.decode("S.test") - - with self.subTest(msg=f"invalid prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.StringLocation.decode("A.test.t") - - with self.subTest(msg=f"decode"): - location, identifier = cadence.StringLocation.decode("S.test.T") - self.assertEqual( - cadence.StringLocation("test"), - location, - ) - self.assertEqual( - "T", - identifier, - ) - - def testDecodeFlowLocation(self): - with self.subTest(msg=f"missing prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.FlowLocation.decode("") - - with self.subTest(msg=f"missing location"): - with self.assertRaises(CadenceEncodingError): - cadence.FlowLocation.decode("flow") - - with self.subTest(msg=f"invalid prefix"): - with self.assertRaises(CadenceEncodingError): - cadence.FlowLocation.decode("A.test") - - with self.subTest(msg=f"decode"): - location, identifier = cadence.FlowLocation.decode("flow.test") - self.assertEqual( - cadence.FlowLocation(), - location, - ) - self.assertEqual( - "test", - identifier, - )