From 19a25a33fc948448ae6c9271787bb00fe35cce63 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 12 Nov 2024 13:00:31 +0200 Subject: [PATCH 1/3] Add explicit enums implementation --- src/smartcontracts/codec/binary.spec.ts | 62 ++++++++++------ src/smartcontracts/codec/binary.ts | 41 ++++++---- src/smartcontracts/codec/explicit-enum.ts | 33 +++++++++ src/smartcontracts/nativeSerializer.spec.ts | 57 ++++++++++++++ src/smartcontracts/nativeSerializer.ts | 18 +++++ .../typesystem/abiRegistry.spec.ts | 6 +- src/smartcontracts/typesystem/abiRegistry.ts | 13 +++- .../typesystem/explicit-enum.ts | 74 +++++++++++++++++++ src/smartcontracts/typesystem/index.ts | 5 +- src/smartcontracts/typesystem/matchers.ts | 24 ++++-- src/smartcontracts/typesystem/typeMapper.ts | 12 +++ src/testdata/basic-features.abi.json | 17 +++++ 12 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 src/smartcontracts/codec/explicit-enum.ts create mode 100644 src/smartcontracts/typesystem/explicit-enum.ts diff --git a/src/smartcontracts/codec/binary.spec.ts b/src/smartcontracts/codec/binary.spec.ts index 33df7e6c..8e6f92ec 100644 --- a/src/smartcontracts/codec/binary.spec.ts +++ b/src/smartcontracts/codec/binary.spec.ts @@ -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(); @@ -175,6 +176,21 @@ describe("test binary codec (basic)", () => { ]); assert.deepEqual(codec.decodeTopLevel(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(Buffer.from([...length, ...payload]), enumType), [ + enumValue, + 8, + ]); + assert.deepEqual(codec.decodeTopLevel(Buffer.from(payload), enumType), enumValue); + }); }); describe("test binary codec (advanced)", () => { diff --git a/src/smartcontracts/codec/binary.ts b/src/smartcontracts/codec/binary.ts index 0c5bbdb2..bd6be3ad 100644 --- a/src/smartcontracts/codec/binary.ts +++ b/src/smartcontracts/codec/binary.ts @@ -1,8 +1,17 @@ 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, @@ -10,27 +19,21 @@ import { 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; @@ -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; @@ -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); } @@ -68,6 +73,7 @@ export class BinaryCodec { onStruct: () => this.structCodec.decodeTopLevel(buffer, type), onTuple: () => this.tupleCodec.decodeTopLevel(buffer, type), onEnum: () => this.enumCodec.decodeTopLevel(buffer, type), + onExplicitEnum: () => this.explicitEnumCodec.decodeTopLevel(buffer, type), onManagedDecimal: () => this.managedDecimalCodec.decodeTopLevel(buffer, type), onManagedDecimalSigned: () => this.managedDecimalSignedCodec.decodeTopLevel(buffer, type), @@ -87,6 +93,7 @@ export class BinaryCodec { onStruct: () => this.structCodec.decodeNested(buffer, type), onTuple: () => this.tupleCodec.decodeNested(buffer, type), onEnum: () => this.enumCodec.decodeNested(buffer, type), + onExplicitEnum: () => this.explicitEnumCodec.decodeNested(buffer, type), onManagedDecimal: () => this.managedDecimalCodec.decodeNested(buffer, type), onManagedDecimalSigned: () => this.managedDecimalSignedCodec.decodeNested(buffer, type), @@ -106,6 +113,7 @@ export class BinaryCodec { onStruct: () => this.structCodec.encodeNested(typedValue), onTuple: () => this.tupleCodec.encodeNested(typedValue), onEnum: () => this.enumCodec.encodeNested(typedValue), + onExplicitEnum: () => this.explicitEnumCodec.encodeNested(typedValue), onManagedDecimal: () => this.managedDecimalCodec.encodeNested(typedValue), onManagedDecimalSigned: () => this.managedDecimalSignedCodec.encodeNested(typedValue), @@ -123,6 +131,7 @@ export class BinaryCodec { onStruct: () => this.structCodec.encodeTopLevel(typedValue), onTuple: () => this.tupleCodec.encodeTopLevel(typedValue), onEnum: () => this.enumCodec.encodeTopLevel(typedValue), + onExplicitEnum: () => this.explicitEnumCodec.encodeTopLevel(typedValue), onManagedDecimal: () => this.managedDecimalCodec.encodeTopLevel(typedValue), onManagedDecimalSigned: () => this.managedDecimalSignedCodec.encodeTopLevel(typedValue), diff --git a/src/smartcontracts/codec/explicit-enum.ts b/src/smartcontracts/codec/explicit-enum.ts new file mode 100644 index 00000000..a88eef81 --- /dev/null +++ b/src/smartcontracts/codec/explicit-enum.ts @@ -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; + } +} diff --git a/src/smartcontracts/nativeSerializer.spec.ts b/src/smartcontracts/nativeSerializer.spec.ts index 70411eec..f34ed2ca 100644 --- a/src/smartcontracts/nativeSerializer.spec.ts +++ b/src/smartcontracts/nativeSerializer.spec.ts @@ -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", + }, + ], + }, }, }); @@ -590,6 +603,50 @@ 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"); + + // Simple enum by name + const p1 = "completed"; + // Enum with a single field + + const typedValues = NativeSerializer.nativeToTypedValues([p1], endpoint); + + console.log(typedValues[0].valueOf()); + assert.deepEqual(typedValues[0].getType(), enumType); + assert.deepEqual(typedValues[0].valueOf(), { name: "completed" }); + }); + it("should getArgumentsCardinality", async () => { const abi = AbiRegistry.create({ endpoints: [ diff --git a/src/smartcontracts/nativeSerializer.ts b/src/smartcontracts/nativeSerializer.ts index 59e8c492..e1c10c61 100644 --- a/src/smartcontracts/nativeSerializer.ts +++ b/src/smartcontracts/nativeSerializer.ts @@ -22,6 +22,8 @@ import { EndpointParameterDefinition, EnumType, EnumValue, + ExplicitEnumType, + ExplicitEnumValue, Field, I16Type, I16Value, @@ -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); } @@ -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()); diff --git a/src/smartcontracts/typesystem/abiRegistry.spec.ts b/src/smartcontracts/typesystem/abiRegistry.spec.ts index 4832690f..216cddba 100644 --- a/src/smartcontracts/typesystem/abiRegistry.spec.ts +++ b/src/smartcontracts/typesystem/abiRegistry.spec.ts @@ -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 () => { diff --git a/src/smartcontracts/typesystem/abiRegistry.ts b/src/smartcontracts/typesystem/abiRegistry.ts index 538c7d14..60a6c46f 100644 --- a/src/smartcontracts/typesystem/abiRegistry.ts +++ b/src/smartcontracts/typesystem/abiRegistry.ts @@ -3,6 +3,7 @@ import { guardValueIsSetWithMessage } from "../../utils"; import { EndpointDefinition, EndpointParameterDefinition } from "./endpoint"; import { EnumType } from "./enum"; import { EventDefinition, EventTopicDefinition } from "./event"; +import { ExplicitEnumType } from "./explicit-enum"; import { StructType } from "./struct"; import { TypeMapper } from "./typeMapper"; import { CustomType } from "./types"; @@ -63,8 +64,12 @@ export class AbiRegistry { if (typeDefinition.type == "struct") { customTypes.push(StructType.fromJSON({ name: customTypeName, fields: typeDefinition.fields })); - } else if (typeDefinition.type == "enum" || typeDefinition.type == "explicit-enum") { + } else if (typeDefinition.type == "enum") { customTypes.push(EnumType.fromJSON({ name: customTypeName, variants: typeDefinition.variants })); + } else if (typeDefinition.type == "explicit-enum") { + customTypes.push( + ExplicitEnumType.fromJSON({ name: customTypeName, variants: typeDefinition.variants }), + ); } else { throw new errors.ErrTypingSystem(`Cannot handle custom type: ${customTypeName}`); } @@ -107,6 +112,12 @@ export class AbiRegistry { return result!; } + getExplicitEnum(name: string): ExplicitEnumType { + const result = this.customTypes.find((e) => e.getName() == name && e.hasExactClass(ExplicitEnumType.ClassName)); + guardValueIsSetWithMessage(`enum [${name}] not found`, result); + return result!; + } + getEnums(names: string[]): EnumType[] { return names.map((name) => this.getEnum(name)); } diff --git a/src/smartcontracts/typesystem/explicit-enum.ts b/src/smartcontracts/typesystem/explicit-enum.ts new file mode 100644 index 00000000..264e18fa --- /dev/null +++ b/src/smartcontracts/typesystem/explicit-enum.ts @@ -0,0 +1,74 @@ +import { guardValueIsSet } from "../../utils"; +import { CustomType, TypedValue } from "./types"; + +export class ExplicitEnumType extends CustomType { + static ClassName = "ExplicitEnumType"; + readonly variants: ExplicitEnumVariantDefinition[] = []; + + constructor(name: string, variants: ExplicitEnumVariantDefinition[]) { + super(name); + this.variants = variants; + } + + getClassName(): string { + return ExplicitEnumType.ClassName; + } + + static fromJSON(json: { name: string; variants: any[] }): ExplicitEnumType { + const variants = json.variants.map((variant) => ExplicitEnumVariantDefinition.fromJSON(variant)); + return new ExplicitEnumType(json.name, variants); + } + + getVariantByName(name: string): ExplicitEnumVariantDefinition { + let result = this.variants.find((e) => e.name == name); + guardValueIsSet(`variant by name (${name})`, result); + return result!; + } +} + +export class ExplicitEnumVariantDefinition { + readonly name: string; + + constructor(name: string) { + this.name = name; + } + + static fromJSON(json: { name: string }): ExplicitEnumVariantDefinition { + return new ExplicitEnumVariantDefinition(json.name); + } +} + +export class ExplicitEnumValue extends TypedValue { + static ClassName = "ExplicitEnumValue"; + readonly name: string; + + constructor(type: ExplicitEnumType, variant: ExplicitEnumVariantDefinition) { + super(type); + this.name = variant.name; + } + + getClassName(): string { + return ExplicitEnumValue.ClassName; + } + + /** + * Utility (named constructor) to create a simple (i.e. without fields) enum value. + */ + static fromName(type: ExplicitEnumType, name: string): ExplicitEnumValue { + let variant = type.getVariantByName(name); + + return new ExplicitEnumValue(type, variant); + } + + equals(other: ExplicitEnumValue): boolean { + if (!this.getType().equals(other.getType())) { + return false; + } + + return this.name == other.name; + } + + valueOf() { + return { name: this.name }; + } +} diff --git a/src/smartcontracts/typesystem/index.ts b/src/smartcontracts/typesystem/index.ts index 7f5dd2da..936c4032 100644 --- a/src/smartcontracts/typesystem/index.ts +++ b/src/smartcontracts/typesystem/index.ts @@ -12,11 +12,14 @@ export * from "./codeMetadata"; export * from "./composite"; export * from "./endpoint"; export * from "./enum"; +export * from "./explicit-enum"; export * from "./factory"; export * from "./fields"; export * from "./generic"; export * from "./genericArray"; export * from "./h256"; +export * from "./managedDecimal"; +export * from "./managedDecimalSigned"; export * from "./matchers"; export * from "./nothing"; export * from "./numerical"; @@ -28,5 +31,3 @@ export * from "./typeExpressionParser"; export * from "./typeMapper"; export * from "./types"; export * from "./variadic"; -export * from "./managedDecimal"; -export * from "./managedDecimalSigned"; diff --git a/src/smartcontracts/typesystem/matchers.ts b/src/smartcontracts/typesystem/matchers.ts index 7a901d75..a877a966 100644 --- a/src/smartcontracts/typesystem/matchers.ts +++ b/src/smartcontracts/typesystem/matchers.ts @@ -4,19 +4,19 @@ import { BooleanType, BooleanValue } from "./boolean"; import { BytesType, BytesValue } from "./bytes"; import { CodeMetadataType, CodeMetadataValue } from "./codeMetadata"; import { EnumType, EnumValue } from "./enum"; -import { OptionType, OptionValue, List, ListType } from "./generic"; +import { ExplicitEnumType, ExplicitEnumValue } from "./explicit-enum"; +import { List, ListType, OptionType, OptionValue } from "./generic"; +import { ArrayVec, ArrayVecType } from "./genericArray"; import { H256Type, H256Value } from "./h256"; -import { NumericalType, NumericalValue } from "./numerical"; +import { ManagedDecimalType, ManagedDecimalValue } from "./managedDecimal"; +import { ManagedDecimalSignedType, ManagedDecimalSignedValue } from "./managedDecimalSigned"; import { NothingType, NothingValue } from "./nothing"; +import { NumericalType, NumericalValue } from "./numerical"; +import { StringType, StringValue } from "./string"; import { Struct, StructType } from "./struct"; import { TokenIdentifierType, TokenIdentifierValue } from "./tokenIdentifier"; import { Tuple, TupleType } from "./tuple"; -import { Type, PrimitiveType, PrimitiveValue } from "./types"; -import { ArrayVec, ArrayVecType } from "./genericArray"; -import { TypedValue } from "./types"; -import { StringType, StringValue } from "./string"; -import { ManagedDecimalType, ManagedDecimalValue } from "./managedDecimal"; -import { ManagedDecimalSignedType, ManagedDecimalSignedValue } from "./managedDecimalSigned"; +import { PrimitiveType, PrimitiveValue, Type, TypedValue } from "./types"; // TODO: Extend functionality or rename wrt. restricted / reduced functionality (not all types are handled: composite, variadic). export function onTypeSelect( @@ -29,6 +29,7 @@ export function onTypeSelect( onStruct: () => TResult; onTuple: () => TResult; onEnum: () => TResult; + onExplicitEnum: () => TResult; onManagedDecimal: () => TResult; onManagedDecimalSigned: () => TResult; onOther?: () => TResult; @@ -55,6 +56,9 @@ export function onTypeSelect( if (type.hasExactClass(EnumType.ClassName)) { return selectors.onEnum(); } + if (type.hasExactClass(ExplicitEnumType.ClassName)) { + return selectors.onExplicitEnum(); + } if (type.hasExactClass(ManagedDecimalType.ClassName)) { return selectors.onManagedDecimal(); @@ -81,6 +85,7 @@ export function onTypedValueSelect( onStruct: () => TResult; onTuple: () => TResult; onEnum: () => TResult; + onExplicitEnum: () => TResult; onManagedDecimal: () => TResult; onManagedDecimalSigned: () => TResult; onOther?: () => TResult; @@ -107,6 +112,9 @@ export function onTypedValueSelect( if (value.hasExactClass(EnumValue.ClassName)) { return selectors.onEnum(); } + if (value.hasExactClass(ExplicitEnumValue.ClassName)) { + return selectors.onExplicitEnum(); + } if (value.hasExactClass(ManagedDecimalValue.ClassName)) { return selectors.onManagedDecimal(); } diff --git a/src/smartcontracts/typesystem/typeMapper.ts b/src/smartcontracts/typesystem/typeMapper.ts index e8ca9a4d..8eebdf5f 100644 --- a/src/smartcontracts/typesystem/typeMapper.ts +++ b/src/smartcontracts/typesystem/typeMapper.ts @@ -6,6 +6,7 @@ import { BytesType } from "./bytes"; import { CodeMetadataType } from "./codeMetadata"; import { CompositeType } from "./composite"; import { EnumType, EnumVariantDefinition } from "./enum"; +import { ExplicitEnumType, ExplicitEnumVariantDefinition } from "./explicit-enum"; import { FieldDefinition } from "./fields"; import { ListType, OptionType } from "./generic"; import { ArrayVecType } from "./genericArray"; @@ -160,6 +161,11 @@ export class TypeMapper { return this.mapEnumType(type); } + if (type.hasExactClass(ExplicitEnumType.ClassName)) { + // This will call mapType() recursively, for all the explicit enum variant fields. + return this.mapExplicitEnumType(type); + } + if (type.hasExactClass(StructType.ClassName)) { // This will call mapType() recursively, for all the struct's fields. return this.mapStructType(type); @@ -204,6 +210,12 @@ export class TypeMapper { return mappedEnum; } + private mapExplicitEnumType(type: ExplicitEnumType): ExplicitEnumType { + let variants = type.variants.map((variant) => new ExplicitEnumVariantDefinition(variant.name)); + let mappedEnum = new ExplicitEnumType(type.getName(), variants); + return mappedEnum; + } + private mappedFields(definitions: FieldDefinition[]): FieldDefinition[] { return definitions.map( (definition) => new FieldDefinition(definition.name, definition.description, this.mapType(definition.type)), diff --git a/src/testdata/basic-features.abi.json b/src/testdata/basic-features.abi.json index f30c5500..733da10f 100644 --- a/src/testdata/basic-features.abi.json +++ b/src/testdata/basic-features.abi.json @@ -6038,6 +6038,23 @@ "Helper type to explore encode/decode errors." ] }, + "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" + } + ] + }, "EsdtTokenPayment": { "type": "struct", "fields": [ From cf0de4051e72866b643ed3f2480dae2d531d6993 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 12 Nov 2024 13:21:50 +0200 Subject: [PATCH 2/3] Remove console logs and add explicit enums tests --- src/smartcontracts/nativeSerializer.spec.ts | 10 +++------ .../typesystem/explicit-enum.spec.ts | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 src/smartcontracts/typesystem/explicit-enum.spec.ts diff --git a/src/smartcontracts/nativeSerializer.spec.ts b/src/smartcontracts/nativeSerializer.spec.ts index f34ed2ca..060ee3a8 100644 --- a/src/smartcontracts/nativeSerializer.spec.ts +++ b/src/smartcontracts/nativeSerializer.spec.ts @@ -635,16 +635,12 @@ describe("test native serializer", () => { const endpoint = abiRegistry.getEndpoint("foo"); const enumType = abiRegistry.getExplicitEnum("OperationCompletionStatus"); + const enumString = "completed"; - // Simple enum by name - const p1 = "completed"; - // Enum with a single field - - const typedValues = NativeSerializer.nativeToTypedValues([p1], endpoint); + const typedValues = NativeSerializer.nativeToTypedValues([enumString], endpoint); - console.log(typedValues[0].valueOf()); assert.deepEqual(typedValues[0].getType(), enumType); - assert.deepEqual(typedValues[0].valueOf(), { name: "completed" }); + assert.deepEqual(typedValues[0].valueOf(), { name: enumString }); }); it("should getArgumentsCardinality", async () => { diff --git a/src/smartcontracts/typesystem/explicit-enum.spec.ts b/src/smartcontracts/typesystem/explicit-enum.spec.ts new file mode 100644 index 00000000..8c726aec --- /dev/null +++ b/src/smartcontracts/typesystem/explicit-enum.spec.ts @@ -0,0 +1,22 @@ +import { assert } from "chai"; +import { ExplicitEnumType, ExplicitEnumValue, ExplicitEnumVariantDefinition } from "./explicit-enum"; + +describe("test explicit-enums", () => { + it("should get valueOf()", () => { + // Define variants + let greenVariant = new ExplicitEnumVariantDefinition("Green"); + + let orangeVariant = new ExplicitEnumVariantDefinition("Orange"); + + let yellowVariant = new ExplicitEnumVariantDefinition("Yellow"); + + // Define enum type + let explicitEnumType = new ExplicitEnumType("Colour", [greenVariant, orangeVariant, yellowVariant]); + + // Create enum values + let green = new ExplicitEnumValue(explicitEnumType, greenVariant); + + // Test valueOf() + assert.deepEqual(green.valueOf(), { name: "Green" }); + }); +}); From 9a6bd369cc662243936b0e0a75dbae5a6b002c0b Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 12 Nov 2024 13:22:42 +0200 Subject: [PATCH 3/3] Bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccfde954..8d9c32d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "13.14.2", + "version": "13.15.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "13.14.2", + "version": "13.15.0", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", diff --git a/package.json b/package.json index 681ff735..abe0b1d3 100644 --- a/package.json +++ b/package.json @@ -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",