-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add managed decimal support (as in the rust framework) #477
Changes from all commits
615034c
c59657d
ad8d581
ab476cf
9dceb18
06b85fe
e35ad8f
6065dee
0d9e486
5a11659
42bf1c6
dada969
73670bd
1f65ffe
8e054c6
9135302
33a875d
14b2f5f
275bb49
0ca88e9
3838dc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,21 @@ | ||
export class TypeFormula { | ||
name: string; | ||
metadata: any; | ||
typeParameters: TypeFormula[]; | ||
|
||
constructor(name: string, typeParameters: TypeFormula[]) { | ||
constructor(name: string, typeParameters: TypeFormula[], metadata?: any) { | ||
this.name = name; | ||
this.typeParameters = typeParameters; | ||
this.metadata = metadata; | ||
} | ||
|
||
toString(): string { | ||
if (this.typeParameters.length > 0) { | ||
const typeParameters = this.typeParameters.map((typeParameter) => typeParameter.toString()).join(", "); | ||
return `${this.name}<${typeParameters}>`; | ||
} else { | ||
return this.name; | ||
} | ||
const hasTypeParameters = this.typeParameters.length > 0; | ||
const typeParameters = hasTypeParameters | ||
? `<${this.typeParameters.map((tp) => tp.toString()).join(", ")}>` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if move down when there will be no typeParameters we will have <> with empty string |
||
: ""; | ||
const baseName = `${this.name}${typeParameters}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you intentionally left out the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <> is already added when we build the variable typeParameters |
||
|
||
return this.metadata !== undefined ? `${baseName}*${this.metadata}*` : baseName; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All good to append the metadata after an asterisk 👍 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import BigNumber from "bignumber.js"; | ||
import { BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem"; | ||
import { BinaryCodec } from "./binary"; | ||
import { bufferToBigInt } from "./utils"; | ||
import { SizeOfU32 } from "./constants"; | ||
|
||
export class ManagedDecimalCodec { | ||
private readonly binaryCodec: BinaryCodec; | ||
|
||
constructor(binaryCodec: BinaryCodec) { | ||
this.binaryCodec = binaryCodec; | ||
} | ||
|
||
decodeNested(buffer: Buffer, type: ManagedDecimalType): [ManagedDecimalValue, 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: ManagedDecimalType): ManagedDecimalValue { | ||
if (buffer.length === 0) { | ||
return new ManagedDecimalValue(new BigNumber(0), 0); | ||
} | ||
|
||
if (type.isVariable()) { | ||
const bigUintSize = buffer.length - SizeOfU32; | ||
|
||
const value = new BigNumber(buffer.slice(0, bigUintSize).toString("hex"), 16); | ||
const scale = buffer.readUInt32BE(bigUintSize); | ||
|
||
return new ManagedDecimalValue(value, scale); | ||
} | ||
|
||
const value = bufferToBigInt(buffer); | ||
const metadata = type.getMetadata(); | ||
const scale = typeof metadata === "number" ? metadata : 0; | ||
return new ManagedDecimalValue(value, scale); | ||
} | ||
|
||
encodeNested(value: ManagedDecimalValue): Buffer { | ||
let buffers: Buffer[] = []; | ||
if (value.isVariable()) { | ||
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())))); | ||
} | ||
return Buffer.concat(buffers); | ||
} | ||
|
||
encodeTopLevel(value: ManagedDecimalValue): Buffer { | ||
return this.encodeNested(value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import { ResultsParser } from "./resultsParser"; | |
import { TransactionWatcher } from "../transactionWatcher"; | ||
import { SmartContractQueriesController } from "../smartContractQueriesController"; | ||
import { QueryRunnerAdapter } from "../adapters/queryRunnerAdapter"; | ||
import { ManagedDecimalSignedValue, ManagedDecimalValue } from "./typesystem"; | ||
|
||
describe("test smart contract interactor", function () { | ||
let provider = createLocalnetProvider(); | ||
|
@@ -184,6 +185,118 @@ 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 () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test is very nice. For education purposes, maybe it's better to have it using the new transactions factory. |
||
this.timeout(140000); | ||
|
||
let abiRegistry = await loadAbiRegistry("src/testdata/basic-features.abi.json"); | ||
let contract = new SmartContract({ abi: abiRegistry }); | ||
let controller = new ContractController(provider); | ||
|
||
let network = await provider.getNetworkConfig(); | ||
await alice.sync(provider); | ||
|
||
// Deploy the contract | ||
let deployTransaction = await prepareDeployment({ | ||
contract: contract, | ||
deployer: alice, | ||
codePath: "src/testdata/basic-features.wasm", | ||
gasLimit: 600000000, | ||
initArguments: [], | ||
chainID: network.ChainID, | ||
}); | ||
|
||
let { | ||
bundle: { returnCode }, | ||
} = await controller.deploy(deployTransaction); | ||
assert.isTrue(returnCode.isSuccess()); | ||
|
||
let returnEgldInteraction = <Interaction>( | ||
contract.methods | ||
.returns_egld_decimal([]) | ||
.withGasLimit(10000000) | ||
.withChainID(network.ChainID) | ||
.withSender(alice.address) | ||
.withValue(1) | ||
); | ||
|
||
// returnEgld() | ||
let returnEgldTransaction = returnEgldInteraction | ||
.withSender(alice.address) | ||
.useThenIncrementNonceOf(alice.account) | ||
.buildTransaction(); | ||
|
||
let additionInteraction = <Interaction>contract.methods | ||
.managed_decimal_addition([new ManagedDecimalValue(2, 2), new ManagedDecimalValue(3, 2)]) | ||
.withGasLimit(10000000) | ||
.withChainID(network.ChainID) | ||
.withSender(alice.address) | ||
.withValue(0); | ||
|
||
// addition() | ||
let additionTransaction = additionInteraction | ||
.withSender(alice.address) | ||
.useThenIncrementNonceOf(alice.account) | ||
.buildTransaction(); | ||
|
||
// log | ||
let mdLnInteraction = <Interaction>contract.methods | ||
.managed_decimal_ln([new ManagedDecimalValue(23000000000, 9)]) | ||
.withGasLimit(10000000) | ||
.withChainID(network.ChainID) | ||
.withSender(alice.address) | ||
.withValue(0); | ||
|
||
// mdLn() | ||
let mdLnTransaction = mdLnInteraction | ||
.withSender(alice.address) | ||
.useThenIncrementNonceOf(alice.account) | ||
.buildTransaction(); | ||
|
||
let additionVarInteraction = <Interaction>contract.methods | ||
.managed_decimal_addition_var([ | ||
new ManagedDecimalValue(378298000000, 9, true), | ||
new ManagedDecimalValue(378298000000, 9, true), | ||
]) | ||
.withGasLimit(50000000) | ||
.withChainID(network.ChainID) | ||
.withSender(alice.address) | ||
.withValue(0); | ||
|
||
// addition() | ||
let additionVarTransaction = additionVarInteraction | ||
.withSender(alice.address) | ||
.useThenIncrementNonceOf(alice.account) | ||
.buildTransaction(); | ||
|
||
// returnEgld() | ||
await signTransaction({ transaction: returnEgldTransaction, wallet: alice }); | ||
let { bundle: bundleEgld } = await controller.execute(returnEgldInteraction, returnEgldTransaction); | ||
assert.isTrue(bundleEgld.returnCode.equals(ReturnCode.Ok)); | ||
assert.lengthOf(bundleEgld.values, 1); | ||
assert.deepEqual(bundleEgld.values[0], new ManagedDecimalValue(1, 18)); | ||
|
||
// addition with const decimals() | ||
await signTransaction({ transaction: additionTransaction, wallet: alice }); | ||
let { bundle: bundleAdditionConst } = await controller.execute(additionInteraction, additionTransaction); | ||
assert.isTrue(bundleAdditionConst.returnCode.equals(ReturnCode.Ok)); | ||
assert.lengthOf(bundleAdditionConst.values, 1); | ||
assert.deepEqual(bundleAdditionConst.values[0], new ManagedDecimalValue(5, 2)); | ||
|
||
// log | ||
await signTransaction({ transaction: mdLnTransaction, wallet: alice }); | ||
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)); | ||
|
||
// 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)); | ||
}); | ||
|
||
it("should interact with 'counter' (local testnet)", async function () { | ||
this.timeout(120000); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe change type to
string
here, as wellThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here we will let any because is the general object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you think it would be better to use
object
type instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it can be also a primitive that's why I think any is a better option here