From 7db0a4a96f6aade942e37adda931c349389ba166 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 4 Sep 2023 20:17:12 +0300 Subject: [PATCH] smart contract transaction intents factory --- ...tContractTransactionIntentsFactory.spec.ts | 48 ++++++++++ .../smartContractTransactionIntentsFactory.ts | 94 ++++++++++++++++--- .../transactionIntentBuilder.ts | 7 +- 3 files changed, 133 insertions(+), 16 deletions(-) diff --git a/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.spec.ts b/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.spec.ts index fbcd147c..1b02baa7 100644 --- a/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.spec.ts +++ b/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.spec.ts @@ -57,4 +57,52 @@ describe("test smart contract intents factory", function () { assert.deepEqual(deployIntent, abiDeployIntent); }); + + it("should build execute intent", async function () { + const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); + const func = "add"; + const gasLimit = 6000000; + const args = [new U32Value(7)]; + + const deployIntent = smartContractIntentsFactory.createTransactionIntentForExecute(sender, contract, func, gasLimit, args); + const abiDeployIntent = abiSmartContractIntentsFactory.createTransactionIntentForExecute(sender, contract, func, gasLimit, args); + + assert.equal(deployIntent.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + assert.equal(deployIntent.receiver, "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); + + assert.isDefined(deployIntent.data); + let decoder = new TextDecoder(); + assert.equal(decoder.decode(deployIntent.data), "add@07"); + + assert.equal(deployIntent.gasLimit.valueOf(), 6059000); + assert.equal(deployIntent.value, 0); + + assert.deepEqual(deployIntent, abiDeployIntent); + }); + + it("should build upgrade intent", async function () { + const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); + const gasLimit = 6000000; + const args = [new U32Value(0)]; + + const deployIntent = smartContractIntentsFactory.createTransactionIntentForUpgrade(sender, contract, adderByteCode.valueOf(), gasLimit, args); + const abiDeployIntent = abiSmartContractIntentsFactory.createTransactionIntentForUpgrade(sender, contract, adderByteCode.valueOf(), gasLimit, args); + + assert.equal(deployIntent.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + assert.equal(deployIntent.receiver, "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); + assert.isDefined(deployIntent.data); + + if (deployIntent.data) { + let decoder = new TextDecoder(); + assert(decoder.decode(deployIntent.data).startsWith("upgradeContract@", 0)); + + const expectedGasLimit = 6000000 + 50000 + 1500 * deployIntent.data.length; + assert.equal(deployIntent.gasLimit.valueOf(), expectedGasLimit); + } + assert.equal(deployIntent.value, 0); + + assert.deepEqual(deployIntent, abiDeployIntent); + }); }); diff --git a/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.ts b/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.ts index ec95be6d..89fb089c 100644 --- a/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.ts +++ b/src/transactionIntentsFactories/smartContractTransactionIntentsFactory.ts @@ -1,7 +1,7 @@ import { BigNumber } from "bignumber.js"; import { IAddress } from "../interface"; import { TransactionIntent } from "../transactionIntent"; -import { AbiRegistry, ArgSerializer, CodeMetadata, TypedValue } from "../smartcontracts"; +import { AbiRegistry, ArgSerializer, CodeMetadata, EndpointDefinition, TypedValue } from "../smartcontracts"; import { byteArrayToHex } from "../utils.codec"; import { CONTRACT_DEPLOY_ADDRESS, VM_TYPE_WASM_VM } from "../constants"; import { NativeSerializer } from "../smartcontracts/nativeSerializer"; @@ -29,10 +29,10 @@ export class SmartContractTransactionIntentsFactory { bytecode: Uint8Array, gasLimit: BigNumber.Value, args: any[], - isUpgradeable: boolean = true, - isReadable: boolean = true, - isPayable: boolean = false, - isPayableBySmartContract: boolean = true + isUpgradeable = true, + isReadable = true, + isPayable = false, + isPayableBySmartContract = true ): TransactionIntent { const metadata = new CodeMetadata(isUpgradeable, isReadable, isPayable, isPayableBySmartContract); let parts = [ @@ -41,7 +41,14 @@ export class SmartContractTransactionIntentsFactory { metadata.toString() ]; - parts = parts.concat(this.argsToStrings(args)); + let preparedArgs: string[]; + if (this.abiRegistry) { + preparedArgs = this.argsToStrings(args, this.abiRegistry.constructorDefinition) + } + else { + preparedArgs = this.argsToStrings(args) + } + parts = parts.concat(preparedArgs); return new TransactionIntentBuilder( this.config, @@ -49,14 +56,77 @@ export class SmartContractTransactionIntentsFactory { Address.fromBech32(CONTRACT_DEPLOY_ADDRESS), parts, gasLimit - ).build() + ).build(); } - private argsToStrings(args: any[]): string[] { - if (this.abiRegistry !== undefined) { - const constructorDefinition = this.abiRegistry.constructorDefinition - const typedArgs = NativeSerializer.nativeToTypedValues(args, constructorDefinition) - return new ArgSerializer().valuesToStrings(args); + createTransactionIntentForExecute( + sender: IAddress, + contractAddress: IAddress, + func: string, + gasLimit: BigNumber.Value, + args: any[] = [] + ): TransactionIntent { + let parts: string[] = [func]; + + let preparedArgs: string[]; + if (this.abiRegistry) { + preparedArgs = this.argsToStrings(args, this.abiRegistry.getEndpoint(func)); + } + else { + preparedArgs = this.argsToStrings(args); + } + parts = parts.concat(preparedArgs); + + return new TransactionIntentBuilder( + this.config, + sender, + contractAddress, + parts, + gasLimit + ).build(); + } + + createTransactionIntentForUpgrade( + sender: IAddress, + contract: IAddress, + bytecode: Uint8Array, + gasLimit: BigNumber.Value, + args: any[], + isUpgradeable = true, + isReadable = true, + isPayable = false, + isPayableBySmartContract = true + ): TransactionIntent { + const metadata = new CodeMetadata(isUpgradeable, isReadable, isPayable, isPayableBySmartContract); + + let parts = [ + "upgradeContract", + byteArrayToHex(bytecode), + metadata.toString() + ]; + + let preparedArgs: string[]; + if (this.abiRegistry) { + preparedArgs = this.argsToStrings(args, this.abiRegistry.constructorDefinition) + } + else { + preparedArgs = this.argsToStrings(args) + } + parts = parts.concat(preparedArgs); + + return new TransactionIntentBuilder( + this.config, + sender, + contract, + parts, + gasLimit + ).build(); + } + + private argsToStrings(args: any[], endpoint?: EndpointDefinition): string[] { + if (endpoint) { + const typedArgs = NativeSerializer.nativeToTypedValues(args, endpoint) + return new ArgSerializer().valuesToStrings(typedArgs); } if (this.areArgsOfTypedValue(args)) { diff --git a/src/transactionIntentsFactories/transactionIntentBuilder.ts b/src/transactionIntentsFactories/transactionIntentBuilder.ts index 5f80c4b0..ffa1f42b 100644 --- a/src/transactionIntentsFactories/transactionIntentBuilder.ts +++ b/src/transactionIntentsFactories/transactionIntentBuilder.ts @@ -32,16 +32,15 @@ export class TransactionIntentBuilder { return gasLimit; } - private buildTransactionPayload(dataParts: string[]): TransactionPayload { - const data = dataParts.join(ARGUMENTS_SEPARATOR); + private buildTransactionPayload(): TransactionPayload { + const data = this.dataParts.join(ARGUMENTS_SEPARATOR); return new TransactionPayload(data); } build(): TransactionIntent { - const data = this.buildTransactionPayload(this.dataParts) + const data = this.buildTransactionPayload() const gasLimit = this.computeGasLimit(data, this.executionGasLimit); - return new TransactionIntent( this.sender.bech32(), this.receiver.bech32(),