Skip to content

Commit

Permalink
Merge pull request #115 from multiversx/update-23-1
Browse files Browse the repository at this point in the history
ABI: handle CodeMetadataValue
  • Loading branch information
andreibancioiu authored Oct 23, 2024
2 parents a062beb + f51b444 commit faf9d90
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 1 deletion.
2 changes: 2 additions & 0 deletions multiversx_sdk/abi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from multiversx_sdk.abi.biguint_value import BigUIntValue
from multiversx_sdk.abi.bool_value import BoolValue
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.fields import Field
from multiversx_sdk.abi.list_value import ListValue
Expand All @@ -32,6 +33,7 @@
"BigUIntValue",
"BoolValue",
"BytesValue",
"CodeMetadataValue",
"EnumValue",
"Field",
"ListValue",
Expand Down
3 changes: 2 additions & 1 deletion multiversx_sdk/abi/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from multiversx_sdk.abi.biguint_value import BigUIntValue
from multiversx_sdk.abi.bool_value import BoolValue
from multiversx_sdk.abi.bytes_value import BytesValue
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.fields import Field
Expand Down Expand Up @@ -282,7 +283,7 @@ def _create_prototype(self, type_formula: TypeFormula) -> Any:
if name == "EgldOrEsdtTokenIdentifier":
return TokenIdentifierValue()
if name == "CodeMetadata":
return BytesValue()
return CodeMetadataValue()
if name == "tuple":
return TupleValue([self._create_prototype(type_parameter) for type_parameter in type_formula.type_parameters])
if name == "Option":
Expand Down
46 changes: 46 additions & 0 deletions multiversx_sdk/abi/code_metadata_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import io
from typing import Any

from multiversx_sdk.abi.shared import read_bytes_exactly
from multiversx_sdk.core.code_metadata import (CODE_METADATA_LENGTH,
CodeMetadata)


class CodeMetadataValue:
def __init__(self, value: bytes = b"") -> None:
self.value = value

@classmethod
def new_from_code_metadata(cls, code_metadata: CodeMetadata) -> "CodeMetadataValue":
return cls(code_metadata.serialize())

def encode_nested(self, writer: io.BytesIO):
writer.write(self.value)

def encode_top_level(self, writer: io.BytesIO):
writer.write(self.value)

def decode_nested(self, reader: io.BytesIO):
length = CODE_METADATA_LENGTH
data = read_bytes_exactly(reader, length)
self.value = data

def decode_top_level(self, data: bytes):
self.value = data

def set_payload(self, value: Any):
if isinstance(value, bytes):
self.value = CodeMetadata.new_from_bytes(value).serialize()
elif isinstance(value, CodeMetadata):
self.value = value.serialize()
else:
raise ValueError(f"cannot set payload for code metadata (should be either a CodeMetadata or bytes, but got: {type(value)})")

def get_payload(self) -> Any:
return self.value

def __eq__(self, other: Any) -> bool:
return isinstance(other, CodeMetadataValue) and self.value == other.value

def __bytes__(self) -> bytes:
return self.value
33 changes: 33 additions & 0 deletions multiversx_sdk/abi/code_metadata_value_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import re

import pytest

from multiversx_sdk.abi.code_metadata_value import CodeMetadataValue
from multiversx_sdk.core.code_metadata import CodeMetadata


def test_new_from_code_metadata():
value = CodeMetadataValue.new_from_code_metadata(CodeMetadata())
assert value.get_payload() == bytes([0x05, 0x00])

value = CodeMetadataValue.new_from_code_metadata(CodeMetadata(upgradeable=True, readable=True, payable=True, payable_by_contract=True))
assert value.get_payload() == bytes([0x05, 0x06])


def test_set_payload_and_get_payload():
# Simple
value = CodeMetadataValue()
value.set_payload(bytes([0x05, 0x00]))
assert value.get_payload() == bytes([0x05, 0x00])

# With CodeMetadata
value = CodeMetadataValue()
value.set_payload(CodeMetadata(upgradeable=True, readable=True, payable=True, payable_by_contract=True))
assert value.get_payload() == bytes([0x05, 0x06])

# With errors
with pytest.raises(ValueError, match=re.escape("cannot set payload for code metadata (should be either a CodeMetadata or bytes, but got: <class 'dict'>)")):
CodeMetadataValue().set_payload({})

with pytest.raises(ValueError, match="code metadata buffer has length 4, expected 2"):
CodeMetadataValue().set_payload(bytes([0, 1, 2, 3]))
17 changes: 17 additions & 0 deletions multiversx_sdk/core/code_metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from enum import Enum

CODE_METADATA_LENGTH = 2


class ByteZero(Enum):
Upgradeable = 1
Expand All @@ -20,6 +22,21 @@ def __init__(self, upgradeable: bool = True, readable: bool = True, payable: boo
self.payable = payable
self.payable_by_contract = payable_by_contract

@classmethod
def new_from_bytes(cls, data: bytes) -> "CodeMetadata":
if len(data) != CODE_METADATA_LENGTH:
raise ValueError(f"code metadata buffer has length {len(data)}, expected {CODE_METADATA_LENGTH}")

byte_zero = data[0]
byte_one = data[1]

upgradeable = (byte_zero & ByteZero.Upgradeable.value) != 0
readable = (byte_zero & ByteZero.Readable.value) != 0
payable = (byte_one & ByteOne.Payable.value) != 0
payable_by_contract = (byte_one & ByteOne.PayableByContract.value) != 0

return cls(upgradeable, readable, payable, payable_by_contract)

def serialize(self) -> bytes:
data = bytearray([0, 0])

Expand Down
31 changes: 31 additions & 0 deletions multiversx_sdk/core/code_metadata_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@


import pytest

from multiversx_sdk.core.code_metadata import CodeMetadata


def test_code_metadata_new_from_bytes():
metadata = CodeMetadata.new_from_bytes(bytes([0x05, 0x00]))
assert metadata.upgradeable == True
assert metadata.readable == True
assert metadata.payable == False
assert metadata.payable_by_contract == False

metadata = CodeMetadata.new_from_bytes(bytes([0x05, 0x06]))
assert metadata.upgradeable == True
assert metadata.readable == True
assert metadata.payable == True
assert metadata.payable_by_contract == True

metadata = CodeMetadata.new_from_bytes(bytes([0x05, 0x04]))
assert metadata.upgradeable == True
assert metadata.readable == True
assert metadata.payable == False
assert metadata.payable_by_contract == True

metadata = CodeMetadata.new_from_bytes(bytes([0x00, 0x00]))
assert metadata.upgradeable == False
assert metadata.readable == False
assert metadata.payable == False
assert metadata.payable_by_contract == False

with pytest.raises(ValueError, match="code metadata buffer has length 4, expected 2"):
CodeMetadata.new_from_bytes(bytes([0x00, 0x01, 0x02, 0x03]))


def test_code_metadata_serialize():
assert CodeMetadata().serialize() == bytes([0x05, 0x00])
assert CodeMetadata(upgradeable=True, readable=True).serialize() == bytes([0x05, 0x00])
Expand Down

0 comments on commit faf9d90

Please sign in to comment.