Skip to content

Commit

Permalink
Merge pull request #529 from multiversx/TOOL-328-fix-explicit-enums
Browse files Browse the repository at this point in the history
Add explicit enums implementation
  • Loading branch information
danielailie authored Nov 12, 2024
2 parents 7a1480b + 9a6bd36 commit 9de1d6a
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 57 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@multiversx/sdk-core",
"version": "13.14.2",
"version": "13.15.0",
"description": "MultiversX SDK for JavaScript and TypeScript",
"author": "MultiversX",
"homepage": "https://multiversx.com",
Expand Down
62 changes: 39 additions & 23 deletions src/smartcontracts/codec/binary.spec.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
import * as errors from "../../errors";
import BigNumber from "bignumber.js";
import { assert } from "chai";
import { BinaryCodec, BinaryCodecConstraints } from "./binary";
import { Address } from "../../address";
import * as errors from "../../errors";
import {
AddressType,
AddressValue,
ArrayVec,
ArrayVecType,
BigIntType,
BigIntValue,
BigUIntType,
BigUIntValue,
BooleanType,
BooleanValue,
EnumType,
EnumValue,
EnumVariantDefinition,
Field,
I16Type,
I16Value,
I32Type,
I32Value,
I64Type,
I64Value,
I8Type,
I8Value,
List,
ListType,
NumericalType,
NumericalValue,
StringType,
StringValue,
Struct,
Field,
StructType,
TokenIdentifierType,
TokenIdentifierValue,
TypedValue,
U16Type,
U16Value,
U32Type,
U32Value,
U64Type,
U64Value,
U8Type,
U8Value,
List,
ListType,
EnumType,
EnumVariantDefinition,
EnumValue,
ArrayVec,
ArrayVecType,
U16Value,
TokenIdentifierType,
TokenIdentifierValue,
StringValue,
StringType,
BigIntValue,
I64Value,
I32Value,
I16Value,
I8Value,
} from "../typesystem";
import { isMsbOne } from "./utils";
import { Address } from "../../address";
import { BytesType, BytesValue } from "../typesystem/bytes";
import BigNumber from "bignumber.js";
import { ExplicitEnumType, ExplicitEnumValue, ExplicitEnumVariantDefinition } from "../typesystem/explicit-enum";
import { FieldDefinition } from "../typesystem/fields";
import { BinaryCodec, BinaryCodecConstraints } from "./binary";
import { isMsbOne } from "./utils";

describe("test binary codec (basic)", () => {
let codec = new BinaryCodec();
Expand Down Expand Up @@ -175,6 +176,21 @@ describe("test binary codec (basic)", () => {
]);
assert.deepEqual(codec.decodeTopLevel<StringValue>(Buffer.from(payload), new StringType()), stringValue);
});

it("should create explicit-enums, encode and decode", async () => {
let length = [0x00, 0x00, 0x00, 0x04];
let payload = [0x74, 0x65, 0x73, 0x74];
let enumType = new ExplicitEnumType("Colour", [new ExplicitEnumVariantDefinition("test")]);
let enumValue = ExplicitEnumValue.fromName(enumType, "test");

assert.deepEqual(codec.encodeNested(enumValue), Buffer.from([...length, ...payload]));
assert.deepEqual(codec.encodeTopLevel(enumValue), Buffer.from(payload));
assert.deepEqual(codec.decodeNested<ExplicitEnumValue>(Buffer.from([...length, ...payload]), enumType), [
enumValue,
8,
]);
assert.deepEqual(codec.decodeTopLevel<ExplicitEnumValue>(Buffer.from(payload), enumType), enumValue);
});
});

describe("test binary codec (advanced)", () => {
Expand Down
41 changes: 25 additions & 16 deletions src/smartcontracts/codec/binary.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import * as errors from "../../errors";
import { guardTrue } from "../../utils";
import {
Type,
ArrayVec,
ArrayVecType,
EnumType,
EnumValue,
ExplicitEnumType,
ExplicitEnumValue,
List,
ManagedDecimalSignedType,
ManagedDecimalSignedValue,
ManagedDecimalType,
ManagedDecimalValue,
onTypedValueSelect,
onTypeSelect,
OptionValue,
PrimitiveType,
PrimitiveValue,
Struct,
StructType,
TypedValue,
EnumValue,
TupleType,
Tuple,
ArrayVecType,
ArrayVec,
ManagedDecimalType,
ManagedDecimalValue,
ManagedDecimalSignedType,
ManagedDecimalSignedValue,
TupleType,
Type,
TypedValue,
} from "../typesystem";
import { guardTrue } from "../../utils";
import { ArrayVecBinaryCodec } from "./arrayVec";
import { EnumBinaryCodec } from "./enum";
import { ExplicitEnumBinaryCodec } from "./explicit-enum";
import { ListBinaryCodec } from "./list";
import { ManagedDecimalCodec } from "./managedDecimal";
import { ManagedDecimalSignedCodec } from "./managedDecimalSigned";
import { OptionValueBinaryCodec } from "./option";
import { PrimitiveBinaryCodec } from "./primitive";
import { ListBinaryCodec } from "./list";
import { StructBinaryCodec } from "./struct";
import { EnumBinaryCodec } from "./enum";
import { TupleBinaryCodec } from "./tuple";
import { ArrayVecBinaryCodec } from "./arrayVec";
import { ManagedDecimalCodec } from "./managedDecimal";
import { ManagedDecimalSignedCodec } from "./managedDecimalSigned";

export class BinaryCodec {
readonly constraints: BinaryCodecConstraints;
Expand All @@ -41,6 +44,7 @@ export class BinaryCodec {
private readonly structCodec: StructBinaryCodec;
private readonly tupleCodec: TupleBinaryCodec;
private readonly enumCodec: EnumBinaryCodec;
private readonly explicitEnumCodec: ExplicitEnumBinaryCodec;
private readonly managedDecimalCodec: ManagedDecimalCodec;
private readonly managedDecimalSignedCodec: ManagedDecimalSignedCodec;

Expand All @@ -53,6 +57,7 @@ export class BinaryCodec {
this.structCodec = new StructBinaryCodec(this);
this.tupleCodec = new TupleBinaryCodec(this);
this.enumCodec = new EnumBinaryCodec(this);
this.explicitEnumCodec = new ExplicitEnumBinaryCodec();
this.managedDecimalCodec = new ManagedDecimalCodec(this);
this.managedDecimalSignedCodec = new ManagedDecimalSignedCodec(this);
}
Expand All @@ -68,6 +73,7 @@ export class BinaryCodec {
onStruct: () => this.structCodec.decodeTopLevel(buffer, <StructType>type),
onTuple: () => this.tupleCodec.decodeTopLevel(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeTopLevel(buffer, <EnumType>type),
onExplicitEnum: () => this.explicitEnumCodec.decodeTopLevel(buffer, <ExplicitEnumType>type),
onManagedDecimal: () => this.managedDecimalCodec.decodeTopLevel(buffer, <ManagedDecimalType>type),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.decodeTopLevel(buffer, <ManagedDecimalSignedType>type),
Expand All @@ -87,6 +93,7 @@ export class BinaryCodec {
onStruct: () => this.structCodec.decodeNested(buffer, <StructType>type),
onTuple: () => this.tupleCodec.decodeNested(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeNested(buffer, <EnumType>type),
onExplicitEnum: () => this.explicitEnumCodec.decodeNested(buffer, <ExplicitEnumType>type),
onManagedDecimal: () => this.managedDecimalCodec.decodeNested(buffer, <ManagedDecimalType>type),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.decodeNested(buffer, <ManagedDecimalSignedType>type),
Expand All @@ -106,6 +113,7 @@ export class BinaryCodec {
onStruct: () => this.structCodec.encodeNested(<Struct>typedValue),
onTuple: () => this.tupleCodec.encodeNested(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeNested(<EnumValue>typedValue),
onExplicitEnum: () => this.explicitEnumCodec.encodeNested(<ExplicitEnumValue>typedValue),
onManagedDecimal: () => this.managedDecimalCodec.encodeNested(<ManagedDecimalValue>typedValue),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.encodeNested(<ManagedDecimalSignedValue>typedValue),
Expand All @@ -123,6 +131,7 @@ export class BinaryCodec {
onStruct: () => this.structCodec.encodeTopLevel(<Struct>typedValue),
onTuple: () => this.tupleCodec.encodeTopLevel(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeTopLevel(<EnumValue>typedValue),
onExplicitEnum: () => this.explicitEnumCodec.encodeTopLevel(<ExplicitEnumValue>typedValue),
onManagedDecimal: () => this.managedDecimalCodec.encodeTopLevel(<ManagedDecimalValue>typedValue),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.encodeTopLevel(<ManagedDecimalSignedValue>typedValue),
Expand Down
33 changes: 33 additions & 0 deletions src/smartcontracts/codec/explicit-enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { StringValue } from "../typesystem";
import { ExplicitEnumType, ExplicitEnumValue, ExplicitEnumVariantDefinition } from "../typesystem/explicit-enum";
import { StringBinaryCodec } from "./string";

export class ExplicitEnumBinaryCodec {
private readonly stringCodec: StringBinaryCodec;

constructor() {
this.stringCodec = new StringBinaryCodec();
}

decodeTopLevel(buffer: Buffer, type: ExplicitEnumType): ExplicitEnumValue {
const stringValue = this.stringCodec.decodeTopLevel(buffer);
return new ExplicitEnumValue(type, new ExplicitEnumVariantDefinition(stringValue.valueOf()));
}

decodeNested(buffer: Buffer, type: ExplicitEnumType): [ExplicitEnumValue, number] {
const [value, length] = this.stringCodec.decodeNested(buffer);
const enumValue = new ExplicitEnumValue(type, new ExplicitEnumVariantDefinition(value.valueOf()));

return [enumValue, length];
}

encodeNested(enumValue: ExplicitEnumValue): Buffer {
const buffer = this.stringCodec.encodeNested(new StringValue(enumValue.valueOf().name));
return buffer;
}

encodeTopLevel(enumValue: ExplicitEnumValue): Buffer {
const buffer = this.stringCodec.encodeTopLevel(new StringValue(enumValue.valueOf().name));
return buffer;
}
}
53 changes: 53 additions & 0 deletions src/smartcontracts/nativeSerializer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,19 @@ describe("test native serializer", () => {
},
],
},
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",
},
],
},
},
});

Expand Down Expand Up @@ -590,6 +603,46 @@ describe("test native serializer", () => {
assert.deepEqual(typedValues[3].valueOf(), { name: "Else", fields: [new BigNumber(42), new BigNumber(43)] });
});

it("should perform type inference (explicit-enums)", async () => {
const abiRegistry = AbiRegistry.create({
endpoints: [
{
name: "foo",
inputs: [
{
type: "OperationCompletionStatus",
},
],
outputs: [],
},
],
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",
},
],
},
},
});

const endpoint = abiRegistry.getEndpoint("foo");
const enumType = abiRegistry.getExplicitEnum("OperationCompletionStatus");
const enumString = "completed";

const typedValues = NativeSerializer.nativeToTypedValues([enumString], endpoint);

assert.deepEqual(typedValues[0].getType(), enumType);
assert.deepEqual(typedValues[0].valueOf(), { name: enumString });
});

it("should getArgumentsCardinality", async () => {
const abi = AbiRegistry.create({
endpoints: [
Expand Down
18 changes: 18 additions & 0 deletions src/smartcontracts/nativeSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
EndpointParameterDefinition,
EnumType,
EnumValue,
ExplicitEnumType,
ExplicitEnumValue,
Field,
I16Type,
I16Value,
Expand Down Expand Up @@ -203,6 +205,9 @@ export namespace NativeSerializer {
if (type instanceof EnumType) {
return toEnumValue(value, type, errorContext);
}
if (type instanceof ExplicitEnumType) {
return toExplicitEnumValue(value, type, errorContext);
}
if (type instanceof ManagedDecimalType) {
return toManagedDecimal(value, type, errorContext);
}
Expand Down Expand Up @@ -337,6 +342,19 @@ export namespace NativeSerializer {
errorContext.throwError(`(function: toEnumValue) unsupported native type ${typeof native}`);
}

function toExplicitEnumValue(native: any, type: ExplicitEnumType, errorContext: ArgumentErrorContext): TypedValue {
if (typeof native === "string") {
return ExplicitEnumValue.fromName(type, native);
}
if (typeof native === "object") {
errorContext.guardHasField(native, "name");
const variant = type.getVariantByName(native.name);

return new ExplicitEnumValue(type, variant);
}
errorContext.throwError(`(function: toExplicitEnumValue) unsupported native type ${typeof native}`);
}

function toManagedDecimal(native: any, type: ManagedDecimalType, errorContext: ArgumentErrorContext): TypedValue {
if (typeof native === "object") {
return new ManagedDecimalValue(native[0], native[1], type.isVariable());
Expand Down
6 changes: 2 additions & 4 deletions src/smartcontracts/typesystem/abiRegistry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,13 @@ describe("test abi registry", () => {
});

it("should load ABI explicit-enum", async () => {
const registry = await loadAbiRegistry("src/testdata/explicit-enum.abi.json");
const registry = await loadAbiRegistry("src/testdata/basic-features.abi.json");

const enumType = registry.getEnum("OperationCompletionStatus");
const enumType = registry.getExplicitEnum("OperationCompletionStatus");

assert.deepEqual(enumType.variants[0].name, "completed");
assert.deepEqual(enumType.variants[0].discriminant, 0);

assert.deepEqual(enumType.variants[1].name, "interrupted");
assert.deepEqual(enumType.variants[1].discriminant, 1);
});

it("should load abi with title for endpoint", async () => {
Expand Down
Loading

0 comments on commit 9de1d6a

Please sign in to comment.