Skip to content

Commit

Permalink
Merge pull request #116 from multiversx/update-23-2
Browse files Browse the repository at this point in the history
Implement explicit enums
  • Loading branch information
andreibancioiu authored Oct 24, 2024
2 parents faf9d90 + daed12e commit bb1b7be
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 64 deletions.
2 changes: 2 additions & 0 deletions multiversx_sdk/abi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from multiversx_sdk.abi.bytes_value import BytesValue
from multiversx_sdk.abi.code_metadata_value import CodeMetadataValue
from multiversx_sdk.abi.enum_value import EnumValue
from multiversx_sdk.abi.explicit_enum_value import ExplicitEnumValue
from multiversx_sdk.abi.fields import Field
from multiversx_sdk.abi.list_value import ListValue
from multiversx_sdk.abi.multi_value import MultiValue
Expand Down Expand Up @@ -35,6 +36,7 @@
"BytesValue",
"CodeMetadataValue",
"EnumValue",
"ExplicitEnumValue",
"Field",
"ListValue",
"OptionValue",
Expand Down
7 changes: 7 additions & 0 deletions multiversx_sdk/abi/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from multiversx_sdk.abi.code_metadata_value import CodeMetadataValue
from multiversx_sdk.abi.counted_variadic_values import CountedVariadicValues
from multiversx_sdk.abi.enum_value import EnumValue
from multiversx_sdk.abi.explicit_enum_value import ExplicitEnumValue
from multiversx_sdk.abi.fields import Field
from multiversx_sdk.abi.interface import IPayloadHolder
from multiversx_sdk.abi.list_value import ListValue
Expand Down Expand Up @@ -85,6 +86,9 @@ def _create_custom_type_prototype(self, name: str) -> Any:
if name in self.definition.types.enums:
definition = self.definition.types.enums[name]
return self._create_enum_prototype(definition)
if name in self.definition.types.explicit_enums:
definition = self.definition.types.explicit_enums[name]
return self._create_explicit_enum_prototype()
if name in self.definition.types.structs:
definition = self.definition.types.structs[name]
return self._create_struct_prototype(definition)
Expand All @@ -94,6 +98,9 @@ def _create_custom_type_prototype(self, name: str) -> Any:
def _create_enum_prototype(self, enum_definition: EnumDefinition) -> Any:
return EnumValue(fields_provider=lambda discriminant: self._provide_fields_for_enum_prototype(discriminant, enum_definition))

def _create_explicit_enum_prototype(self) -> Any:
return ExplicitEnumValue()

def _provide_fields_for_enum_prototype(self, discriminant: int, enum_definition: EnumDefinition) -> List[Field]:
for variant in enum_definition.variants:
if variant.discriminant != discriminant:
Expand Down
42 changes: 42 additions & 0 deletions multiversx_sdk/abi/abi_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,27 +160,33 @@ def __eq__(self, value: object) -> bool:
class TypesDefinitions:
def __init__(self,
enums: List["EnumDefinition"],
explicit_enums: List["ExplicitEnumDefinition"],
structs: List["StructDefinition"]) -> None:
self.enums: Dict[str, EnumDefinition] = {enum.name: enum for enum in enums}
self.explicit_enums: Dict[str, ExplicitEnumDefinition] = {enum.name: enum for enum in explicit_enums}
self.structs: Dict[str, StructDefinition] = {struct.name: struct for struct in structs}

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "TypesDefinitions":
enums: List[EnumDefinition] = []
explicit_enums: List[ExplicitEnumDefinition] = []
structs: List[StructDefinition] = []

for name, definition in data.items():
kind = definition["type"]

if kind == "enum":
enums.append(EnumDefinition.from_dict(name, definition))
elif kind == "explicit-enum":
explicit_enums.append(ExplicitEnumDefinition.from_dict(name, definition))
elif kind == "struct":
structs.append(StructDefinition.from_dict(name, definition))
else:
raise ValueError(f"Unsupported kind of custom type: {kind}")

return cls(
enums=enums,
explicit_enums=explicit_enums,
structs=structs
)

Expand Down Expand Up @@ -228,6 +234,42 @@ def __repr__(self):
return f"EnumVariantDefinition(name={self.name}, discriminant={self.discriminant})"


class ExplicitEnumDefinition:
def __init__(self,
name: str,
variants: List["ExplicitEnumVariantDefinition"]) -> None:
self.name = name
self.variants = variants

@classmethod
def from_dict(cls, name: str, data: Dict[str, Any]) -> "ExplicitEnumDefinition":
variants = [ExplicitEnumVariantDefinition.from_dict(item) for item in data["variants"]]

return cls(
name=name,
variants=variants
)

def __repr__(self):
return f"ExplicitEnumDefinition(name={self.name})"


class ExplicitEnumVariantDefinition:
def __init__(self, name: str) -> None:
self.name = name

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ExplicitEnumVariantDefinition":
fields = [FieldDefinition.from_dict(item) for item in data.get("fields", [])]

return cls(
name=data.get("name", "")
)

def __repr__(self):
return f"ExplicitEnumVariantDefinition(name={self.name})"


class StructDefinition:
def __init__(self,
name: str,
Expand Down
19 changes: 17 additions & 2 deletions multiversx_sdk/abi/abi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from multiversx_sdk.abi.bytes_value import BytesValue
from multiversx_sdk.abi.counted_variadic_values import CountedVariadicValues
from multiversx_sdk.abi.enum_value import EnumValue
from multiversx_sdk.abi.explicit_enum_value import ExplicitEnumValue
from multiversx_sdk.abi.fields import Field
from multiversx_sdk.abi.list_value import ListValue
from multiversx_sdk.abi.option_value import OptionValue
Expand All @@ -21,7 +22,7 @@
testdata = Path(__file__).parent.parent / "testutils" / "testdata"


def test_abi():
def test_abi_adder():
abi = Abi.load(testdata / "adder.abi.json")

assert abi.definition.constructor.name == "constructor"
Expand Down Expand Up @@ -59,9 +60,14 @@ def test_abi():
assert abi.endpoints_prototypes_by_name["add"].output_parameters == []


def test_abi_events():
def test_abi_artificial():
abi = Abi.load(testdata / "artificial.abi.json")

assert len(abi.definition.types.explicit_enums) == 1
assert "OperationCompletionStatus" in abi.definition.types.explicit_enums
assert abi.definition.endpoints[3].outputs[0].type == "OperationCompletionStatus"
assert abi.endpoints_prototypes_by_name["green"].output_parameters[0] == ExplicitEnumValue()

assert len(abi.definition.events) == 1
assert abi.events_prototypes_by_name["firstEvent"].fields[0].value == BigUIntValue()

Expand Down Expand Up @@ -110,6 +116,15 @@ def test_decode_endpoint_output_parameters_artificial_contract():

assert decoded_values == [["UTK-2f80e9", 0, 1000000000000000000]]

decoded_values = abi.decode_endpoint_output_parameters(
endpoint_name="green",
encoded_values=[
"completed".encode(),
]
)

assert decoded_values == ["completed"]


def test_encode_endpoint_input_parameters_multisig_propose_batch():
abi = Abi.load(testdata / "multisig-full.abi.json")
Expand Down
7 changes: 6 additions & 1 deletion multiversx_sdk/abi/enum_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def set_payload(self, value: Any):
raise ValueError("cannot set payload for enum (should be either a dictionary or a list)")

def get_payload(self) -> Any:
obj = SimpleNamespace()
obj = _EnumPayload()

for field in self.fields:
setattr(obj, field.name, field.get_payload())
Expand All @@ -108,3 +108,8 @@ def __iter__(self):

for field in self.fields:
yield (field.name, field.value)


class _EnumPayload(SimpleNamespace):
def __int__(self):
return getattr(self, ENUM_DISCRIMINANT_FIELD_NAME)
4 changes: 4 additions & 0 deletions multiversx_sdk/abi/enum_value_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def provide_fields(discriminant: int) -> List[Field]:
assert value.discriminant == 41
assert value.fields == [Field("a", U32Value(1)), Field("b", BigUIntValue(2))]
assert value.get_payload() == SimpleNamespace(__discriminant__=41, a=1, b=2)
assert int(value.get_payload()) == 41

class Payload:
def __init__(self, c: int, d: int):
Expand All @@ -72,15 +73,18 @@ def __init__(self, c: int, d: int):
assert value.discriminant == 42
assert value.fields == [Field("c", U32Value(3)), Field("d", BigUIntValue(4))]
assert value.get_payload() == SimpleNamespace(__discriminant__=42, c=3, d=4)
assert int(value.get_payload()) == 42

# Then, from dictionary
value.set_payload({"__discriminant__": 43, "e": 5, "f": 6})
assert value.discriminant == 43
assert value.fields == [Field("e", U32Value(5)), Field("f", BigUIntValue(6))]
assert value.get_payload() == SimpleNamespace(__discriminant__=43, e=5, f=6)
assert int(value.get_payload()) == 43

# Finally, from list (first element is the discriminant)
value.set_payload([44, 7, 8])
assert value.discriminant == 44
assert value.fields == [Field("g", U32Value(7)), Field("h", BigUIntValue(8))]
assert value.get_payload() == SimpleNamespace(__discriminant__=44, g=7, h=8)
assert int(value.get_payload()) == 44
11 changes: 11 additions & 0 deletions multiversx_sdk/abi/explicit_enum_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from multiversx_sdk.abi.string_value import StringValue


class ExplicitEnumValue(StringValue):
def __init__(self, value: str = "") -> None:
self.value = value

def __eq__(self, other: Any) -> bool:
return isinstance(other, ExplicitEnumValue) and self.value == other.value
145 changes: 85 additions & 60 deletions multiversx_sdk/testutils/testdata/artificial.abi.json
Original file line number Diff line number Diff line change
@@ -1,68 +1,93 @@
{
"name": "Artificial",
"constructor": {
"inputs": [
{
"name": "a",
"type": "utf-8 string"
}
],
"outputs": []
},
"upgradeConstructor": {
"inputs": [
{
"name": "a",
"type": "u8"
}
],
"outputs": []
},
"endpoints": [
{
"name": "blue",
"mutability": "readonly",
"inputs": [],
"outputs": [
{
"type": "optional<multi<TokenIdentifier,u64,BigUint>>"
}
]
"name": "Artificial",
"constructor": {
"inputs": [
{
"name": "a",
"type": "utf-8 string"
}
],
"outputs": []
},
"upgradeConstructor": {
"inputs": [
{
"name": "a",
"type": "u8"
}
],
"outputs": []
},
{
"name": "yellow",
"mutability": "mutable",
"inputs": [
"endpoints": [
{
"name": "value",
"type": "multi<u32, bytes, bool>"
"name": "blue",
"mutability": "readonly",
"inputs": [],
"outputs": [
{
"type": "optional<multi<TokenIdentifier,u64,BigUint>>"
}
]
},
{
"name": "yellow",
"mutability": "mutable",
"inputs": [
{
"name": "value",
"type": "multi<u32, bytes, bool>"
}
],
"outputs": []
},
{
"name": "orange",
"mutability": "mutable",
"inputs": [
{
"name": "value",
"type": "EgldOrEsdtTokenIdentifier"
}
],
"outputs": []
},
{
"name": "green",
"inputs": [],
"outputs": [
{
"type": "OperationCompletionStatus"
}
]
}
],
"types": {
"OperationCompletionStatus": {
"type": "explicit-enum",
"variants": [
{
"docs": ["indicates that operation was completed"],
"name": "completed"
},
{
"docs": [
"indicates that operation was interrupted prematurely, due to low gas"
],
"name": "interrupted"
}
]
}
],
"outputs": []
},
{
"name": "orange",
"mutability": "mutable",
"inputs": [
"events": [
{
"name": "value",
"type": "EgldOrEsdtTokenIdentifier"
"identifier": "firstEvent",
"inputs": [
{
"name": "result",
"type": "BigUint",
"indexed": true
}
]
}
],
"outputs": []
}
],
"types": {},
"events": [
{
"identifier": "firstEvent",
"inputs": [
{
"name": "result",
"type": "BigUint",
"indexed": true
}
]
}
]
]
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ allow-direct-references = true

[project]
name = "multiversx-sdk"
version = "0.15.0"
version = "0.16.0"
authors = [
{ name="MultiversX" },
]
Expand Down

0 comments on commit bb1b7be

Please sign in to comment.