Skip to content

Commit

Permalink
add enums type inference in NativeSerializer and fix Enum valueof()
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Vergez - PhyloFane committed Jun 19, 2023
1 parent 30f4fbf commit a3305d4
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 3 deletions.
80 changes: 79 additions & 1 deletion src/smartcontracts/nativeSerializer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import BigNumber from "bignumber.js";
import { assert } from "chai";
import { Address } from "../address";
import { NativeSerializer } from "./nativeSerializer";
import { AbiRegistry, AddressType, AddressValue, BigUIntType, BooleanType, BooleanValue, CompositeType, CompositeValue, EndpointDefinition, EndpointModifiers, EndpointParameterDefinition, ListType, NullType, OptionalType, OptionalValue, OptionType, OptionValue, TupleType, U32Type, U64Type, U64Value, U8Type, U8Value, VariadicType, VariadicValue } from "./typesystem";
import { AbiRegistry, AddressType, AddressValue, BigUIntType, BooleanType, BooleanValue, CompositeType, CompositeValue, EndpointDefinition, EndpointModifiers, EndpointParameterDefinition, EnumType, ListType, NullType, OptionalType, OptionalValue, OptionType, OptionValue, TupleType, U32Type, U64Type, U64Value, U8Type, U8Value, VariadicType, VariadicValue } from "./typesystem";
import { BytesType, BytesValue } from "./typesystem/bytes";

describe("test native serializer", () => {
Expand Down Expand Up @@ -248,4 +248,82 @@ describe("test native serializer", () => {
assert.deepEqual(typedValues[2].getType(), new ListType(new TupleType(new U8Type(), new BooleanType())));
assert.deepEqual(typedValues[2].valueOf(), [{ field0: new BigNumber(44), field1: false }, { field0: new BigNumber(45), field1: true }]);
});

it("should perform type inference (enums)", async () => {
const abiRegistry = AbiRegistry.create({
"endpoints": [
{
"name": "foo",
"inputs": [{
"type": "MyEnum",
}, {
"type": "MyEnum",
}, {
"type": "MyEnum",
}, {
"type": "MyEnum",
}],
"outputs": []
}
],
"types": {
"MyEnum": {
"type": "enum",
"variants": [
{
"name": "Nothing",
"discriminant": 0
},
{
"name": "Something",
"discriminant": 1,
"fields": [
{
"name": "0",
"type": "Address"
}
]
},
{
"name": "Else",
"discriminant": 2,
"fields": [
{
"name": "x",
"type": "u64"
},
{
"name": "y",
"type": "u64"
}
]
}
]
},
}
});

const endpoint = abiRegistry.getEndpoint("foo");
const enumType = abiRegistry.getEnum("MyEnum");

// Simple enum by discriminant
const p0 = 0;
// Simple enum by name
const p1 = 'Nothing';
// Enum with a single field
const p2 = { name: 'Something', fields: { 0: 'erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha' } };
// Enum with multiple fields
const p3 = { name: 'Else', fields: { x: 42, y: 43 } };

const typedValues = NativeSerializer.nativeToTypedValues([p0, p1, p2, p3], endpoint);

assert.deepEqual(typedValues[0].getType(), enumType);
assert.deepEqual(typedValues[0].valueOf(), { name: "Nothing", fields: [] });
assert.deepEqual(typedValues[1].getType(), enumType);
assert.deepEqual(typedValues[1].valueOf(), { name: "Nothing", fields: [] });
assert.deepEqual(typedValues[2].getType(), enumType);
assert.deepEqual(typedValues[2].valueOf(), { name: 'Something', fields: [ new Address('erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha') ] });
assert.deepEqual(typedValues[3].getType(), enumType);
assert.deepEqual(typedValues[3].valueOf(), { name: 'Else', fields: [ new BigNumber(42), new BigNumber(43) ] });
});
});
33 changes: 32 additions & 1 deletion src/smartcontracts/nativeSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ErrInvalidArgument } from "../errors";
import { IAddress } from "../interface";
import { numberToPaddedHex } from "../utils.codec";
import { ArgumentErrorContext } from "./argumentErrorContext";
import { AddressType, AddressValue, BigIntType, BigIntValue, BigUIntType, BigUIntValue, BooleanType, BooleanValue, BytesType, BytesValue, CompositeType, CompositeValue, EndpointDefinition, EndpointParameterDefinition, Field, I16Type, I16Value, I32Type, I32Value, I64Type, I64Value, I8Type, I8Value, List, ListType, NumericalType, OptionalType, OptionalValue, OptionType, OptionValue, PrimitiveType, Struct, StructType, TokenIdentifierType, TokenIdentifierValue, Tuple, TupleType, Type, TypedValue, U16Type, U16Value, U32Type, U32Value, U64Type, U64Value, U8Type, U8Value, VariadicType, VariadicValue } from "./typesystem";
import { AddressType, AddressValue, BigIntType, BigIntValue, BigUIntType, BigUIntValue, BooleanType, BooleanValue, BytesType, BytesValue, CompositeType, CompositeValue, EndpointDefinition, EndpointParameterDefinition, EnumType, EnumValue, Field, I16Type, I16Value, I32Type, I32Value, I64Type, I64Value, I8Type, I8Value, List, ListType, NumericalType, OptionalType, OptionalValue, OptionType, OptionValue, PrimitiveType, Struct, StructType, TokenIdentifierType, TokenIdentifierValue, Tuple, TupleType, Type, TypedValue, U16Type, U16Value, U32Type, U32Value, U64Type, U64Value, U8Type, U8Value, VariadicType, VariadicValue } from "./typesystem";

export namespace NativeTypes {
export type NativeBuffer = Buffer | string;
Expand Down Expand Up @@ -113,6 +113,9 @@ export namespace NativeSerializer {
if (type instanceof PrimitiveType) {
return toPrimitive(value, type, errorContext);
}
if (type instanceof EnumType) {
return toEnumValue(value, type, errorContext);
}
errorContext.throwError(`convertToTypedValue: unhandled type ${type}`);
}

Expand Down Expand Up @@ -209,6 +212,34 @@ export namespace NativeSerializer {
errorContext.throwError(`(function: toPrimitive) unsupported type ${type}`);
}

function toEnumValue(native: any, type: EnumType, errorContext: ArgumentErrorContext): TypedValue {
if (typeof native === "number") {
return EnumValue.fromDiscriminant(type, native);
}
if (typeof native === "string") {
return EnumValue.fromName(type, native);
}
if (typeof native === "object") {
errorContext.guardHasField(native, "name");
const variant = type.getVariantByName(native.name);
errorContext.guardHasField(native, "fields");
const nativeFields = native.fields;

const fieldValues = [];
const fields = variant.getFieldsDefinitions();
for (let i = 0; i < fields.length; i++) {
const fieldName = fields[i].name;
errorContext.guardHasField(nativeFields, fieldName);
const fieldNativeValue = nativeFields[fieldName];
const fieldTypedValue = convertToTypedValue(fieldNativeValue, fields[i].type, errorContext);
fieldValues.push(new Field(fieldTypedValue, fieldName));
}

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

// TODO: move logic to typesystem/bytes.ts
function convertNativeToBytesValue(native: NativeTypes.NativeBytes, errorContext: ArgumentErrorContext) {
const innerValue = native.valueOf();
Expand Down
2 changes: 1 addition & 1 deletion src/smartcontracts/typesystem/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class EnumValue extends TypedValue {
valueOf() {
let result: any = { name: this.name, fields: [] };

this.fields.forEach((field) => (result.fields[field.name] = field.value.valueOf()));
this.fields.forEach((field, index) => (result.fields[index] = field.value.valueOf()));

return result;
}
Expand Down

0 comments on commit a3305d4

Please sign in to comment.