Skip to content

Commit

Permalink
Extend managed Decimal
Browse files Browse the repository at this point in the history
  • Loading branch information
danielailie committed Sep 3, 2024
1 parent 615034c commit c59657d
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/abi/typeFormulaParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class TypeFormulaParser {
private acquireTypeWithParameters(stack: any[]): TypeFormula {
const typeParameters = this.acquireTypeParameters(stack);
const typeName = stack.pop();
if (typeName === "ManagedDecimal" || "ManagedDecimalSigned") {
if (typeName === "ManagedDecimal" || typeName === "ManagedDecimalSigned") {
const typeFormula = new TypeFormula(typeName, [], typeParameters[0].name);
return typeFormula;
}
Expand Down
46 changes: 22 additions & 24 deletions src/smartcontracts/codec/managedDecimal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import BigNumber from "bignumber.js";
import { ManagedDecimalType, ManagedDecimalValue, NumericalValue } from "../typesystem";
import { BytesValue } from "../typesystem/bytes";
import { BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U64Value } from "../typesystem";
import { BinaryCodec } from "./binary";
import { cloneBuffer } from "./utils";

Expand All @@ -12,43 +11,42 @@ export class ManagedDecimalCodec {
}

decodeNested(buffer: Buffer, type: ManagedDecimalType): [ManagedDecimalValue, number] {
let [bytesValue, length] = this.binaryCodec.decodeNested(buffer);
console.log({ bytesValue, length });
let [bytesValue, length] = this.binaryCodec.decodeNested(buffer, type);
console.log(11111, { bytesValue });
return [new ManagedDecimalValue(new BigNumber(1), 1), length];
}

decodeTopLevel(buffer: Buffer, type: ManagedDecimalType): ManagedDecimalValue {
let payload = cloneBuffer(buffer);

let empty = buffer.length == 0;
if (empty) {
return new ManagedDecimalValue(new BigNumber(0));
return new ManagedDecimalValue(new BigNumber(0), type.getScale());
}

let isPositive = !type.withSign || isMsbZero(payload);
if (isPositive) {
let value = bufferToBigInt(payload);
return new NumericalValue(type, value);
}
console.log({ bsc: type });
const decimalBuff = Buffer.from(this.binaryCodec.encodeTopLevel(new U64Value(type.getScale())));
const bigUintSize = buffer.length - decimalBuff.length; // Remaining bytes are for BigUInt
console.log({ buffer, l: buffer.length, d: decimalBuff.length, bigUintSize, decimalBuff, sc: type });

// Also see: https://github.com/multiversx/mx-components-big-int/blob/master/twos-complement/twos2bigint.go
flipBufferBitsInPlace(payload);
let value = bufferToBigInt(payload);
let negativeValue = value.multipliedBy(new BigNumber(-1));
let negativeValueMinusOne = negativeValue.minus(new BigNumber(1));
// Read BigUInt (dynamic size)
const bigUintBuffer = payload.slice(0, bigUintSize);
const u64Buffer = payload.slice(bigUintSize, payload.length);
const bigUint = new BigNumber(bigUintBuffer.toString("hex"), 16);
console.log({ payload, bigUintBuffer, u64Buffer });
const u64Value = new U64Value(u64Buffer.toString("hex")).toString();

return new NumericalValue(type, negativeValueMinusOne);
let bytesValue = this.binaryCodec.decodeTopLevel(buffer);
console.log({ bytesValue });
return new ManagedDecimalValue(new BigNumber(1), 1);
console.log({ payload, bigUintBuffer, u64Buffer, u64Value });
return new ManagedDecimalValue(bigUint, type.getScale());
}

encodeNested(value: ManagedDecimalValue): Buffer {
let bytesValue = new BytesValue(Buffer.from(value));
return this.binaryCodec.encodeNested(bytesValue);
let buffers: Buffer[] = [];
buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf()))));
buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new U64Value(value.getScale()))));
return Buffer.concat(buffers);
}

encodeTopLevel(tokenIdentifier: ManagedDecimalValue): Buffer {
return Buffer.from(tokenIdentifier.valueOf());
encodeTopLevel(value: ManagedDecimalValue): Buffer {
return this.encodeNested(value);
}
}
14 changes: 13 additions & 1 deletion src/smartcontracts/typesystem/managedDecimal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export class ManagedDecimalType extends Type {
getClassName(): string {
return ManagedDecimalType.ClassName;
}

getScale(): number {
return this.scale;
}
}

export class ManagedDecimalValue extends TypedValue {
Expand All @@ -30,6 +34,10 @@ export class ManagedDecimalValue extends TypedValue {
return ManagedDecimalValue.ClassName;
}

getScale(): number {
return this.scale;
}

getPrecision(): number {
return this.value.toString(this.scale).replace(".", "").length;
}
Expand All @@ -50,7 +58,7 @@ export class ManagedDecimalValue extends TypedValue {
}

toString(): string {
return this.value.toString();
return this.value.toFixed(this.scale);
}
}

Expand All @@ -66,6 +74,10 @@ export class ManagedDecimalSignedType extends Type {
getClassName(): string {
return ManagedDecimalType.ClassName;
}

getScale(): number {
return this.scale;
}
}

export class ManagedDecimalSignedValue extends TypedValue {
Expand Down
26 changes: 20 additions & 6 deletions src/smartcontracts/typesystem/typeMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FieldDefinition } from "./fields";
import { ListType, OptionType } from "./generic";
import { ArrayVecType } from "./genericArray";
import { H256Type } from "./h256";
import { ManagedDecimalType } from "./managedDecimal";
import { ManagedDecimalSignedType, ManagedDecimalType } from "./managedDecimal";
import { NothingType } from "./nothing";
import {
BigIntType,
Expand All @@ -32,14 +32,15 @@ import { CustomType, Type } from "./types";
import { VariadicType } from "./variadic";

type TypeFactory = (...typeParameters: Type[]) => Type;
type TypeWithMetadataFactory = (...metadata: any) => Type;

export class TypeMapper {
private readonly openTypesFactories: Map<string, TypeFactory>;
private readonly openTypesFactories: Map<string, TypeFactory | TypeWithMetadataFactory>;
private readonly closedTypesMap: Map<string, Type>;
private readonly learnedTypesMap: Map<string, Type>;

constructor(learnedTypes: CustomType[] = []) {
this.openTypesFactories = new Map<string, TypeFactory>([
this.openTypesFactories = new Map<string, TypeFactory | TypeWithMetadataFactory>([
["Option", (...typeParameters: Type[]) => new OptionType(typeParameters[0])],
["List", (...typeParameters: Type[]) => new ListType(typeParameters[0])],
// For the following open generics, we use a slightly different typing than the one defined by mx-sdk-rs (temporary workaround).
Expand Down Expand Up @@ -76,6 +77,7 @@ export class TypeMapper {
["array128", (...typeParameters: Type[]) => new ArrayVecType(128, typeParameters[0])],
["array256", (...typeParameters: Type[]) => new ArrayVecType(256, typeParameters[0])],
["ManagedDecimal", (...metadata: any) => new ManagedDecimalType(parseInt(metadata))],
["ManagedDecimalSigned", (...metadata: any) => new ManagedDecimalSignedType(parseInt(metadata))],
]);

// For closed types, we hold actual type instances instead of type constructors / factories (no type parameters needed).
Expand Down Expand Up @@ -108,7 +110,11 @@ export class TypeMapper {

// Boostrap from previously learned types, if any.
for (const type of learnedTypes) {
this.learnedTypesMap.set(type.getName(), type);
if (type.getName() === "ManagedDecimal" || type.getName() === "ManagedDecimalSigned") {
this.learnedTypesMap.set(`${type.getName()}_${type.getMetadata()}`, type);
} else {
this.learnedTypesMap.set(type.getName(), type);
}
}
}

Expand Down Expand Up @@ -167,8 +173,13 @@ export class TypeMapper {
}

private learnType(type: Type): void {
this.learnedTypesMap.delete(type.getName());
this.learnedTypesMap.set(type.getName(), type);
if (type.getName() === "ManagedDecimal" || type.getName() === "ManagedDecimalSigned") {
this.learnedTypesMap.delete(type.getName());
this.learnedTypesMap.set(`${type.getName()}_${type.getMetadata()}`, type);
} else {
this.learnedTypesMap.delete(type.getName());
this.learnedTypesMap.set(type.getName(), type);
}
}

private mapStructType(type: StructType): StructType {
Expand Down Expand Up @@ -204,6 +215,9 @@ export class TypeMapper {
if (!factory) {
throw new errors.ErrTypingSystem(`Cannot map the generic type "${type.getName()}" to a known type`);
}
if (type.hasMetadata()) {
return factory(type.getMetadata());
}

return factory(...mappedTypeParameters);
}
Expand Down
5 changes: 4 additions & 1 deletion src/smartcontracts/typesystem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export class Type {
metadata?: any,
) {
guardValueIsSet("name", name);

this.name = name;
this.typeParameters = typeParameters;
this.cardinality = cardinality;
Expand Down Expand Up @@ -67,6 +66,10 @@ export class Type {
return this.typeParameters;
}

getMetadata(): any {
return this.metadata;
}

isGenericType(): boolean {
return this.typeParameters.length > 0;
}
Expand Down
19 changes: 19 additions & 0 deletions src/testdata/adder.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@
}
]
},
{
"name": "managed_decimal_subtraction",
"mutability": "mutable",
"inputs": [
{
"name": "first",
"type": "ManagedDecimal<2>"
},
{
"name": "second",
"type": "ManagedDecimal<2>"
}
],
"outputs": [
{
"type": "ManagedDecimal<2>"
}
]
},
{
"docs": [
"Add desired amount to the storage variable."
Expand Down
Binary file added src/testdata/basic-features.wasm
Binary file not shown.

0 comments on commit c59657d

Please sign in to comment.