Skip to content

Commit

Permalink
Add managed decimal signed codec
Browse files Browse the repository at this point in the history
  • Loading branch information
danielailie committed Sep 12, 2024
1 parent 473e284 commit 8ee4554
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 90 deletions.
13 changes: 13 additions & 0 deletions src/smartcontracts/codec/binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
ArrayVec,
ManagedDecimalType,
ManagedDecimalValue,
ManagedDecimalSignedType,
ManagedDecimalSignedValue,
} from "../typesystem";
import { guardTrue } from "../../utils";
import { OptionValueBinaryCodec } from "./option";
Expand All @@ -28,6 +30,7 @@ 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 @@ -39,6 +42,7 @@ export class BinaryCodec {
private readonly tupleCodec: TupleBinaryCodec;
private readonly enumCodec: EnumBinaryCodec;
private readonly managedDecimalCodec: ManagedDecimalCodec;
private readonly managedDecimalSignedCodec: ManagedDecimalSignedCodec;

constructor(constraints: BinaryCodecConstraints | null = null) {
this.constraints = constraints || new BinaryCodecConstraints();
Expand All @@ -50,6 +54,7 @@ export class BinaryCodec {
this.tupleCodec = new TupleBinaryCodec(this);
this.enumCodec = new EnumBinaryCodec(this);
this.managedDecimalCodec = new ManagedDecimalCodec(this);
this.managedDecimalSignedCodec = new ManagedDecimalSignedCodec(this);
}

decodeTopLevel<TResult extends TypedValue = TypedValue>(buffer: Buffer, type: Type): TResult {
Expand All @@ -64,6 +69,8 @@ export class BinaryCodec {
onTuple: () => this.tupleCodec.decodeTopLevel(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeTopLevel(buffer, <EnumType>type),
onManagedDecimal: () => this.managedDecimalCodec.decodeTopLevel(buffer, <ManagedDecimalType>type),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.decodeTopLevel(buffer, <ManagedDecimalSignedType>type),
});

return <TResult>typedValue;
Expand All @@ -81,6 +88,8 @@ export class BinaryCodec {
onTuple: () => this.tupleCodec.decodeNested(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeNested(buffer, <EnumType>type),
onManagedDecimal: () => this.managedDecimalCodec.decodeNested(buffer, <ManagedDecimalType>type),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.decodeNested(buffer, <ManagedDecimalSignedType>type),
});

return [<TResult>typedResult, decodedLength];
Expand All @@ -98,6 +107,8 @@ export class BinaryCodec {
onTuple: () => this.tupleCodec.encodeNested(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeNested(<EnumValue>typedValue),
onManagedDecimal: () => this.managedDecimalCodec.encodeNested(<ManagedDecimalValue>typedValue),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.encodeNested(<ManagedDecimalSignedValue>typedValue),
});
}

Expand All @@ -113,6 +124,8 @@ export class BinaryCodec {
onTuple: () => this.tupleCodec.encodeTopLevel(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeTopLevel(<EnumValue>typedValue),
onManagedDecimal: () => this.managedDecimalCodec.encodeTopLevel(<ManagedDecimalValue>typedValue),
onManagedDecimalSigned: () =>
this.managedDecimalSignedCodec.encodeTopLevel(<ManagedDecimalSignedValue>typedValue),
});
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/smartcontracts/codec/managedDecimal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BigNumber from "bignumber.js";
import { BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem";
import { BigUIntType, BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem";
import { BinaryCodec } from "./binary";
import { bufferToBigInt } from "./utils";
import { SizeOfU32 } from "./constants";
Expand Down Expand Up @@ -27,16 +27,15 @@ export class ManagedDecimalCodec {
if (type.isVariable()) {
const bigUintSize = buffer.length - SizeOfU32;

const value = new BigNumber(buffer.slice(0, bigUintSize).toString("hex"), 16);
const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigUIntType());
const scale = buffer.readUInt32BE(bigUintSize);

return new ManagedDecimalValue(value, scale);
return new ManagedDecimalValue(value.valueOf(), scale);
}

const value = bufferToBigInt(buffer);
const metadata = type.getMetadata();
const scale = typeof metadata === "number" ? metadata : 0;
return new ManagedDecimalValue(value, scale);
const scale = metadata !== "usize" ? parseInt(metadata.toString()) : 0;
return new ManagedDecimalValue(value.shiftedBy(-scale), scale);
}

encodeNested(value: ManagedDecimalValue): Buffer {
Expand All @@ -45,7 +44,9 @@ export class ManagedDecimalCodec {
buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigUIntValue(value.valueOf()))));
buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale()))));
} else {
buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf()))));
buffers.push(
this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf().shiftedBy(value.getScale()))),
);
}
return Buffer.concat(buffers);
}
Expand Down
56 changes: 56 additions & 0 deletions src/smartcontracts/codec/managedDecimalSigned.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import BigNumber from "bignumber.js";
import { BigIntType, BigIntValue, ManagedDecimalSignedType, ManagedDecimalSignedValue, U32Value } from "../typesystem";
import { BinaryCodec } from "./binary";
import { bufferToBigInt } from "./utils";
import { SizeOfU32 } from "./constants";

export class ManagedDecimalSignedCodec {
private readonly binaryCodec: BinaryCodec;

constructor(binaryCodec: BinaryCodec) {
this.binaryCodec = binaryCodec;
}

decodeNested(buffer: Buffer, type: ManagedDecimalSignedType): [ManagedDecimalSignedValue, number] {
const length = buffer.readUInt32BE(0);
const payload = buffer.slice(0, length);

const result = this.decodeTopLevel(payload, type);
return [result, length];
}

decodeTopLevel(buffer: Buffer, type: ManagedDecimalSignedType): ManagedDecimalSignedValue {
if (buffer.length === 0) {
return new ManagedDecimalSignedValue(new BigNumber(0), 0);
}

if (type.isVariable()) {
const bigUintSize = buffer.length - SizeOfU32;

const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigIntType());
const scale = buffer.readUInt32BE(bigUintSize);

return new ManagedDecimalSignedValue(value.valueOf(), scale);
}

const value = bufferToBigInt(buffer);
const metadata = type.getMetadata();
const scale = metadata !== "usize" ? parseInt(metadata.toString()) : 0;
return new ManagedDecimalSignedValue(value.shiftedBy(-scale), scale);
}

encodeNested(value: ManagedDecimalSignedValue): Buffer {
let buffers: Buffer[] = [];
if (value.isVariable()) {
buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigIntValue(new BigNumber("23.000000000")))));
buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale()))));
} else {
buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigIntValue(23.0))));
}
return Buffer.concat(buffers);
}

encodeTopLevel(value: ManagedDecimalSignedValue): Buffer {
return this.encodeNested(value);
}
}
17 changes: 7 additions & 10 deletions src/smartcontracts/interaction.local.net.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ describe("test smart contract interactor", function () {
assert.isTrue(typedBundle.returnCode.equals(ReturnCode.Ok));
});

it("should interact with 'basic-features' (local testnet) using the SmartContractTransactionsFactory", async function () {
it("should interact with 'basic-features' (local testnet)", async function () {
this.timeout(140000);

let abiRegistry = await loadAbiRegistry("src/testdata/basic-features.abi.json");
Expand Down Expand Up @@ -226,21 +226,21 @@ describe("test smart contract interactor", function () {
.buildTransaction();

let additionInteraction = <Interaction>contract.methods
.managed_decimal_addition([new ManagedDecimalValue(2, 2), new ManagedDecimalValue(3, 2)])
.managed_decimal_addition([new ManagedDecimalValue("2.5", 2), new ManagedDecimalValue("2.5", 2)])
.withGasLimit(10000000)
.withChainID(network.ChainID)
.withSender(alice.address)
.withValue(0);

// addition()
// addition();
let additionTransaction = additionInteraction
.withSender(alice.address)
.useThenIncrementNonceOf(alice.account)
.buildTransaction();

// log
let mdLnInteraction = <Interaction>contract.methods
.managed_decimal_ln([new ManagedDecimalValue(23000000000, 9)])
.managed_decimal_ln([new ManagedDecimalValue(23, 9)])
.withGasLimit(10000000)
.withChainID(network.ChainID)
.withSender(alice.address)
Expand All @@ -253,10 +253,7 @@ describe("test smart contract interactor", function () {
.buildTransaction();

let additionVarInteraction = <Interaction>contract.methods
.managed_decimal_addition_var([
new ManagedDecimalValue(378298000000, 9, true),
new ManagedDecimalValue(378298000000, 9, true),
])
.managed_decimal_addition_var([new ManagedDecimalValue(4, 2, true), new ManagedDecimalValue(5, 2, true)])
.withGasLimit(50000000)
.withChainID(network.ChainID)
.withSender(alice.address)
Expand Down Expand Up @@ -287,14 +284,14 @@ describe("test smart contract interactor", function () {
let { bundle: bundleMDLn } = await controller.execute(mdLnInteraction, mdLnTransaction);
assert.isTrue(bundleMDLn.returnCode.equals(ReturnCode.Ok));
assert.lengthOf(bundleMDLn.values, 1);
assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue(3135553845, 9));
assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue(3.135553845, 9));

// addition with var decimals
await signTransaction({ transaction: additionVarTransaction, wallet: alice });
let { bundle: bundleAddition } = await controller.execute(additionVarInteraction, additionVarTransaction);
assert.isTrue(bundleAddition.returnCode.equals(ReturnCode.Ok));
assert.lengthOf(bundleAddition.values, 1);
assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(new BigNumber(6254154138880), 9));
assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(9, 2));
});

it("should interact with 'counter' (local testnet)", async function () {
Expand Down
1 change: 1 addition & 0 deletions src/smartcontracts/typesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from "./typeMapper";
export * from "./types";
export * from "./variadic";
export * from "./managedDecimal";
export * from "./managedDecimalSigned";
65 changes: 0 additions & 65 deletions src/smartcontracts/typesystem/managedDecimal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,68 +69,3 @@ export class ManagedDecimalValue extends TypedValue {
return this.variable;
}
}

export class ManagedDecimalSignedType extends Type {
static ClassName = "ManagedDecimalSignedType";

constructor(metadata: number | "usize") {
super("ManagedDecimalSigned", undefined, undefined, metadata);
}

getClassName(): string {
return ManagedDecimalType.ClassName;
}

getMetadata(): number | "usize" {
return this.metadata;
}

isVariable(): boolean {
return this.metadata == "usize";
}
}

export class ManagedDecimalSignedValue extends TypedValue {
static ClassName = "ManagedDecimalSignedValue";
private readonly value: BigNumber;
private readonly scale: number;
private readonly variable: boolean;

constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) {
super(new ManagedDecimalSignedType(isVariable ? "usize" : scale));
this.value = new BigNumber(value);
this.scale = scale;
this.variable = isVariable;
}

getClassName(): string {
return ManagedDecimalValue.ClassName;
}

getPrecision(): number {
return this.value.toFixed(this.scale).replace(".", "").length;
}

/**
* Returns whether two objects have the same value.
*/
equals(other: ManagedDecimalSignedValue): boolean {
if (this.getPrecision() != other.getPrecision()) {
return false;
}

return new BigNumber(this.value).eq(other.value);
}

valueOf(): BigNumber {
return this.value;
}

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

isVariable(): boolean {
return this.variable;
}
}
71 changes: 71 additions & 0 deletions src/smartcontracts/typesystem/managedDecimalSigned.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import BigNumber from "bignumber.js";
import { Type, TypedValue } from "./types";

export class ManagedDecimalSignedType extends Type {
static ClassName = "ManagedDecimalSignedType";

constructor(metadata: number | "usize") {
super("ManagedDecimalSigned", undefined, undefined, metadata);
}

getClassName(): string {
return ManagedDecimalSignedType.ClassName;
}

getMetadata(): number | "usize" {
return this.metadata;
}

isVariable(): boolean {
return this.metadata == "usize";
}
}

export class ManagedDecimalSignedValue extends TypedValue {
static ClassName = "ManagedDecimalSignedValue";
private readonly value: BigNumber;
private readonly scale: number;
private readonly variable: boolean;

constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) {
super(new ManagedDecimalSignedType(isVariable ? "usize" : scale));
this.value = new BigNumber(value);
this.scale = scale;
this.variable = isVariable;
}

getClassName(): string {
return ManagedDecimalSignedValue.ClassName;
}

getPrecision(): number {
return this.value.toFixed(this.scale).replace(".", "").length;
}

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

/**
* Returns whether two objects have the same value.
*/
equals(other: ManagedDecimalSignedValue): boolean {
if (this.getPrecision() != other.getPrecision()) {
return false;
}

return new BigNumber(this.value).eq(other.value);
}

valueOf(): BigNumber {
return this.value;
}

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

isVariable(): boolean {
return this.variable;
}
}
Loading

0 comments on commit 8ee4554

Please sign in to comment.