diff --git a/package-lock.json b/package-lock.json index 02ff735a..17ee8f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "12.0.1-alpha.1", + "version": "12.0.1-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "12.0.1-alpha.1", + "version": "12.0.1-beta.0", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -34,7 +34,6 @@ "mocha": "9.2.2", "ts-node": "9.1.1", "tslint": "6.1.3", - "typedoc": "0.22.13", "typescript": "4.1.2" } }, @@ -2645,12 +2644,6 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -2852,30 +2845,12 @@ "yallist": "^3.0.2" } }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -3663,17 +3638,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" - } - }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -4086,49 +4050,6 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, - "node_modules/typedoc": { - "version": "0.22.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", - "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", - "dev": true, - "dependencies": { - "glob": "^7.2.0", - "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", - "shiki": "^0.10.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 12.10.0" - }, - "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/typescript": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", @@ -4242,18 +4163,6 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6545,12 +6454,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -6704,24 +6607,12 @@ "yallist": "^3.0.2" } }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -7348,17 +7239,6 @@ "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", "dev": true }, - "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", - "dev": true, - "requires": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" - } - }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -7685,39 +7565,6 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, - "typedoc": { - "version": "0.22.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", - "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", - "dev": true, - "requires": { - "glob": "^7.2.0", - "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", - "shiki": "^0.10.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, "typescript": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", @@ -7801,18 +7648,6 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index e97a151a..98d27878 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "12.0.1-alpha.1", + "version": "12.0.1-beta.0", "description": "MultiversX SDK for JavaScript and TypeScript", "main": "out/index.js", "types": "out/index.d.js", @@ -50,7 +50,6 @@ "mocha": "9.2.2", "ts-node": "9.1.1", "tslint": "6.1.3", - "typedoc": "0.22.13", "typescript": "4.1.2" } } diff --git a/src/_docs/basic.ts b/src/_docs/basic.ts deleted file mode 100644 index 14f6a10e..00000000 --- a/src/_docs/basic.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * This module defines objects for interacting with the blockchain. - * @packageDocumentation - * @module basic - */ - -export * from "../account"; -export * from "../address"; -export * from "../interface"; -export * from "../interfaceOfNetwork"; -export * from "../transaction"; -export * from "../transactionPayload"; -export * from "../transactionWatcher"; -export * from "../logger"; -export * from "../networkParams"; -export * from "../signableMessage"; -export * from "../tokenPayment"; -export * from "../tokenTransferBuilders"; diff --git a/src/_docs/codec.ts b/src/_docs/codec.ts deleted file mode 100644 index fce598ce..00000000 --- a/src/_docs/codec.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * This module defines the binary codecs for smart contract arguments (input and output) - * @packageDocumentation - * @module codec - */ - -export * from "../smartcontracts/codec"; diff --git a/src/_docs/errors.ts b/src/_docs/errors.ts deleted file mode 100644 index e80c550b..00000000 --- a/src/_docs/errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * This module contains the errors raised by the library. - * @packageDocumentation - * @module errors - */ - -export * from "../errors"; diff --git a/src/_docs/smartcontracts.ts b/src/_docs/smartcontracts.ts deleted file mode 100644 index 28cb8199..00000000 --- a/src/_docs/smartcontracts.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * This module defines objects for interacting with smart contracts. - * @packageDocumentation - * @module smartcontracts - */ - -export * from "../smartcontracts/abi"; -export * from "../smartcontracts/argSerializer"; -export * from "../smartcontracts/code"; -export * from "../smartcontracts/codeMetadata"; -export * from "../smartcontracts/function"; -export * from "../smartcontracts/interaction"; -export * from "../smartcontracts/interactionChecker"; -export * from "../smartcontracts/interface"; -export * from "../smartcontracts/nativeSerializer"; -export * from "../smartcontracts/query"; -export * from "../smartcontracts/resultsParser"; -export * from "../smartcontracts/returnCode"; -export * from "../smartcontracts/smartContract"; -export * from "../smartcontracts/transactionPayloadBuilders"; diff --git a/src/_docs/typesystem.ts b/src/_docs/typesystem.ts deleted file mode 100644 index d3489b94..00000000 --- a/src/_docs/typesystem.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * This module defines a custom typesystem, that resembles the typesystem of `mx-sdk-rs`. - * @packageDocumentation - * @module typesystem - */ - -export * from "../smartcontracts/typesystem"; diff --git a/src/constants.ts b/src/constants.ts index 3db572ad..2be83f5e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,6 +9,7 @@ export const ESDT_TRANSFER_FUNCTION_NAME = "ESDTTransfer"; export const ESDTNFT_TRANSFER_FUNCTION_NAME = "ESDTNFTTransfer"; export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer"; export const ESDT_TRANSFER_VALUE = "0"; +export const ARGUMENTS_SEPARATOR = "@"; // Masks needed for checking specific options when Transaction version is not default (1) // 0b0001 - TRANSACTION_OPTIONS_TX_HASH_SIGN (only) diff --git a/src/errors.ts b/src/errors.ts index ae84c6b8..605b890a 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -276,6 +276,15 @@ export class ErrCannotParseContractResults extends Err { } } +/** + * Signals an error when parsing the outcome of a transaction (results and logs). + */ +export class ErrCannotParseTransactionOutcome extends Err { + public constructor(transactionHash: string, message: string) { + super(`cannot parse outcome of transaction ${transactionHash}: ${message}`); + } +} + /** * Signals a generic codec (encode / decode) error. */ diff --git a/src/index.ts b/src/index.ts index 95388526..4fc3d271 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,22 +1,25 @@ require('./globals'); -export * from "./interface"; -export * from "./interfaceOfNetwork"; -export * from "./errors"; export * from "./account"; export * from "./address"; export * from "./asyncTimer"; +export * from "./errors"; export * from "./gasEstimator"; -export * from "./transaction"; -export * from "./transactionFactory"; -export * from "./transactionPayload"; -export * from "./transactionWatcher"; +export * from "./interface"; +export * from "./interfaceOfNetwork"; export * from "./logger"; export * from "./networkParams"; +export * from "./relayedTransactionV1Builder"; +export * from "./relayedTransactionV2Builder"; export * from "./signableMessage"; -export * from "./utils"; +export * from "./smartcontracts"; +export * from "./tokenOperations"; export * from "./tokenPayment"; export * from "./tokenTransferBuilders"; -export * from "./smartcontracts"; -export * from "./relayedTransactionV1Builder"; -export * from "./relayedTransactionV2Builder"; +export * from "./transaction"; +export * from "./transactionFactory"; +export * from "./transactionPayload"; +export * from "./transactionWatcher"; +export * from "./transfersFactory"; +export * from "./utils"; + diff --git a/src/smartcontracts/argSerializer.ts b/src/smartcontracts/argSerializer.ts index 98b382e5..8c7c781e 100644 --- a/src/smartcontracts/argSerializer.ts +++ b/src/smartcontracts/argSerializer.ts @@ -1,10 +1,10 @@ +import { ARGUMENTS_SEPARATOR } from "../constants"; import { BinaryCodec } from "./codec"; import { EndpointParameterDefinition, Type, TypedValue } from "./typesystem"; import { OptionalType, OptionalValue } from "./typesystem/algebraic"; import { CompositeType, CompositeValue } from "./typesystem/composite"; import { VariadicType, VariadicValue } from "./typesystem/variadic"; -export const ArgumentsSeparator = "@"; interface IArgSerializerOptions { codec: ICodec; @@ -43,7 +43,7 @@ export class ArgSerializer { */ stringToBuffers(joinedString: string): Buffer[] { // We also keep the zero-length buffers (they could encode missing options, Option). - return joinedString.split(ArgumentsSeparator).map(item => Buffer.from(item, "hex")); + return joinedString.split(ARGUMENTS_SEPARATOR).map(item => Buffer.from(item, "hex")); } /** @@ -119,7 +119,7 @@ export class ArgSerializer { */ valuesToString(values: TypedValue[]): { argumentsString: string, count: number } { let strings = this.valuesToStrings(values); - let argumentsString = strings.join(ArgumentsSeparator); + let argumentsString = strings.join(ARGUMENTS_SEPARATOR); let count = strings.length; return { argumentsString, count }; } diff --git a/src/smartcontracts/codec/utils.ts b/src/smartcontracts/codec/utils.ts index 5565ac7d..a1ce1225 100644 --- a/src/smartcontracts/codec/utils.ts +++ b/src/smartcontracts/codec/utils.ts @@ -34,13 +34,15 @@ export function bufferToBigInt(buffer: Buffer): BigNumber { return new BigNumber(`0x${hex}`, 16); } -export function bigIntToBuffer(value: BigNumber): Buffer { +export function bigIntToBuffer(value: BigNumber.Value): Buffer { // Currently, in JavaScript, this is the feasible way to achieve reliable, arbitrary-size BigInt to Buffer conversion. let hex = getHexMagnitudeOfBigInt(value); return Buffer.from(hex, "hex"); } -export function getHexMagnitudeOfBigInt(value: BigNumber): string { +export function getHexMagnitudeOfBigInt(value: BigNumber.Value): string { + value = new BigNumber(value); + if (!value) { return ""; } diff --git a/src/smartcontracts/smartContract.ts b/src/smartcontracts/smartContract.ts index a6f8d293..1b67da64 100644 --- a/src/smartcontracts/smartContract.ts +++ b/src/smartcontracts/smartContract.ts @@ -1,20 +1,19 @@ +import BigNumber from "bignumber.js"; import { Address } from "../address"; +import { ErrContractHasNoAddress } from "../errors"; +import { IAddress, INonce } from "../interface"; import { Transaction } from "../transaction"; -import { TransactionPayload } from "../transactionPayload"; -import { CodeMetadata } from "./codeMetadata"; -import { CallArguments, DeployArguments, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; -import { ArwenVirtualMachine } from "./transactionPayloadBuilders"; -import { ContractFunction } from "./function"; -import { Query } from "./query"; -import { SmartContractAbi } from "./abi"; import { guardValueIsSet } from "../utils"; -import { EndpointDefinition, TypedValue } from "./typesystem"; +import { SmartContractAbi } from "./abi"; import { bigIntToBuffer } from "./codec/utils"; -import BigNumber from "bignumber.js"; +import { CodeMetadata } from "./codeMetadata"; +import { ContractFunction } from "./function"; import { Interaction } from "./interaction"; +import { CallArguments, DeployArguments, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; import { NativeSerializer } from "./nativeSerializer"; -import { IAddress, INonce } from "../interface"; -import { ErrContractHasNoAddress } from "../errors"; +import { Query } from "./query"; +import { ArwenVirtualMachine, ContractCallPayloadBuilder, ContractDeployPayloadBuilder, ContractUpgradePayloadBuilder } from "./transactionPayloadBuilders"; +import { EndpointDefinition, TypedValue } from "./typesystem"; const createKeccakHash = require("keccak"); /** @@ -112,7 +111,7 @@ export class SmartContract implements ISmartContract { initArguments = initArguments || []; value = value || 0; - let payload = TransactionPayload.contractDeploy() + let payload = new ContractDeployPayloadBuilder() .setCode(code) .setCodeMetadata(codeMetadata) .setInitArgs(initArguments) @@ -141,7 +140,7 @@ export class SmartContract implements ISmartContract { initArguments = initArguments || []; value = value || 0; - let payload = TransactionPayload.contractUpgrade() + let payload = new ContractUpgradePayloadBuilder() .setCode(code) .setCodeMetadata(codeMetadata) .setInitArgs(initArguments) @@ -169,7 +168,7 @@ export class SmartContract implements ISmartContract { args = args || []; value = value || 0; - let payload = TransactionPayload.contractCall() + let payload = new ContractCallPayloadBuilder() .setFunction(func) .setArgs(args) .build(); diff --git a/src/smartcontracts/typesystem/endpoint.ts b/src/smartcontracts/typesystem/endpoint.ts index a87f56ac..f7cfacd1 100644 --- a/src/smartcontracts/typesystem/endpoint.ts +++ b/src/smartcontracts/typesystem/endpoint.ts @@ -26,7 +26,7 @@ export class EndpointDefinition { mutability: string, payableInTokens: string[], inputs: any[], - outputs: [] + outputs: any[] }): EndpointDefinition { json.name = json.name == null ? NamePlaceholder : json.name; json.payableInTokens = json.payableInTokens || []; diff --git a/src/testutils/networkProviders.ts b/src/testutils/networkProviders.ts index d003620b..75951743 100644 --- a/src/testutils/networkProviders.ts +++ b/src/testutils/networkProviders.ts @@ -1,4 +1,4 @@ -import { ProxyNetworkProvider } from "@multiversx/sdk-network-providers"; +import { ApiNetworkProvider, ProxyNetworkProvider } from "@multiversx/sdk-network-providers"; import { IAddress } from "../interface"; import { IAccountOnNetwork, IContractQueryResponse, INetworkConfig, ITransactionOnNetwork, ITransactionStatus } from "../interfaceOfNetwork"; import { Query } from "../smartcontracts/query"; @@ -8,6 +8,10 @@ export function createLocalnetProvider(): INetworkProvider { return new ProxyNetworkProvider("http://localhost:7950", { timeout: 5000 }); } +export function createTestnetProvider(): INetworkProvider { + return new ApiNetworkProvider("https://testnet-api.multiversx.com", { timeout: 5000 }); +} + export interface INetworkProvider { getNetworkConfig(): Promise; getAccount(address: IAddress): Promise; diff --git a/src/tokenOperations/codec.spec.ts b/src/tokenOperations/codec.spec.ts new file mode 100644 index 00000000..1c2a92e1 --- /dev/null +++ b/src/tokenOperations/codec.spec.ts @@ -0,0 +1,19 @@ +import BigNumber from "bignumber.js"; +import { assert } from "chai"; +import { bigIntToBuffer, bigIntToHex, bufferToBigInt, bufferToHex, stringToBuffer, utf8ToHex } from "./codec"; + +describe("test codec", () => { + it("should properly encode and decode values", () => { + assert.deepEqual(stringToBuffer("hello"), Buffer.from("hello")); + assert.deepEqual(bufferToBigInt(Buffer.from("075bcd15", "hex")), new BigNumber("123456789")); + assert.deepEqual(bufferToBigInt(Buffer.from([])), new BigNumber("0")); + assert.deepEqual(bigIntToBuffer("123456789"), Buffer.from("075bcd15", "hex")); + assert.deepEqual(bigIntToBuffer(0), Buffer.from([])); + assert.equal(bigIntToHex("123456789"), "075bcd15"); + assert.equal(bigIntToHex(0), ""); + assert.equal(utf8ToHex("hello"), "68656c6c6f"); + assert.equal(utf8ToHex(""), ""); + assert.equal(bufferToHex(Buffer.from("hello")), "68656c6c6f"); + assert.equal(bufferToHex(Buffer.from([])), ""); + }); +}); diff --git a/src/tokenOperations/codec.ts b/src/tokenOperations/codec.ts new file mode 100644 index 00000000..3b33abfd --- /dev/null +++ b/src/tokenOperations/codec.ts @@ -0,0 +1,48 @@ +import BigNumber from "bignumber.js"; +import { Address } from "../address"; +import { IAddress } from "../interface"; +import * as contractsCodecUtils from "../smartcontracts/codec/utils"; +import * as codecUtils from "../utils.codec"; + +export function stringToBuffer(value: string): Buffer { + return Buffer.from(value); +} + +export function bufferToBigInt(buffer: Buffer): BigNumber { + if (buffer.length == 0) { + return new BigNumber(0); + } + + return contractsCodecUtils.bufferToBigInt(buffer); +} + +export function bigIntToBuffer(value: BigNumber.Value): Buffer { + if (value == 0) { + return Buffer.from([]); + } + + return contractsCodecUtils.bigIntToBuffer(value); +} + +export function bigIntToHex(value: BigNumber.Value): string { + if (value == 0) { + return ""; + } + + return contractsCodecUtils.getHexMagnitudeOfBigInt(value); +} + +export function utf8ToHex(value: string) { + const hex = Buffer.from(value).toString("hex"); + return codecUtils.zeroPadStringIfOddLength(hex); +} + +export function bufferToHex(value: Buffer) { + const hex = value.toString("hex"); + return codecUtils.zeroPadStringIfOddLength(hex); +} + +export function addressToHex(address: IAddress): string { + const buffer = Address.fromBech32(address.toString()).pubkey(); + return buffer.toString("hex"); +} diff --git a/src/tokenOperations/index.ts b/src/tokenOperations/index.ts new file mode 100644 index 00000000..561db6c7 --- /dev/null +++ b/src/tokenOperations/index.ts @@ -0,0 +1,3 @@ +export * from "./tokenOperationsFactory"; +export * from "./tokenOperationsFactoryConfig"; +export * from "./tokenOperationsOutcomeParser"; diff --git a/src/tokenOperations/tokenOperationsFactory.spec.ts b/src/tokenOperations/tokenOperationsFactory.spec.ts new file mode 100644 index 00000000..6010019a --- /dev/null +++ b/src/tokenOperations/tokenOperationsFactory.spec.ts @@ -0,0 +1,140 @@ +import { assert } from "chai"; +import { loadTestWallets, TestWallet } from "../testutils"; +import { TokenOperationsFactory } from "./tokenOperationsFactory"; +import { TokenOperationsFactoryConfig } from "./tokenOperationsFactoryConfig"; + +describe("test factory", () => { + let frank: TestWallet, grace: TestWallet; + let factory: TokenOperationsFactory; + + before(async function () { + ({ frank, grace } = await loadTestWallets()); + factory = new TokenOperationsFactory(new TokenOperationsFactoryConfig("T")); + }); + + it("should create ", () => { + const transaction = factory.issueFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + initialSupply: 100, + numDecimals: 0, + canFreeze: true, + canWipe: true, + canPause: true, + canMint: true, + canBurn: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "issue@4652414e4b@4652414e4b@64@@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e4d696e74@74727565@63616e4275726e@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@74727565@63616e4164645370656369616c526f6c6573@74727565") + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), frank.address.toString()); + assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + }); + + it("should create ", () => { + const transaction = factory.issueSemiFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "issueSemiFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@74727565@63616e4164645370656369616c526f6c6573@74727565") + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), frank.address.toString()); + assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + }); + + it("should create ", () => { + const transaction = factory.issueNonFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "issueNonFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@74727565@63616e4164645370656369616c526f6c6573@74727565") + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), frank.address.toString()); + assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + }); + + it("should create ", () => { + const transaction = factory.registerMetaESDT({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + numDecimals: 10, + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "registerMetaESDT@4652414e4b@4652414e4b@0a@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@74727565@63616e4164645370656369616c526f6c6573@74727565") + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), frank.address.toString()); + assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + }); + + it("should create ", () => { + const transaction = factory.setSpecialRoleOnNonFungible({ + manager: frank.address, + user: grace.address, + tokenIdentifier: "FRANK-11ce3e", + addRoleNFTCreate: true, + addRoleNFTBurn: false, + addRoleNFTUpdateAttributes: true, + addRoleNFTAddURI: true, + addRoleESDTTransferRole: false, + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "setSpecialRole@4652414e4b2d313163653365@1e8a8b6b49de5b7be10aaa158a5a6a4abb4b56cc08f524bb5e6cd5f211ad3e13@45534454526f6c654e4654437265617465@45534454526f6c654e465455706461746541747472696275746573@45534454526f6c654e4654416464555249"); + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), frank.address.toString()); + assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + }); + + it("should create ", () => { + const transaction = factory.nftCreate({ + creator: grace.address, + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1, + name: `test`, + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + transactionNonce: 42 + }); + + assert.equal(transaction.getData().toString(), "ESDTNFTCreate@4652414e4b2d616139653864@01@74657374@03e8@61626261@74657374@61@62"); + assert.equal(transaction.getNonce(), 42); + assert.equal(transaction.getSender().toString(), grace.address.toString()); + assert.equal(transaction.getReceiver().toString(), grace.address.toString()); + }); +}); diff --git a/src/tokenOperations/tokenOperationsFactory.test.net.spec.ts b/src/tokenOperations/tokenOperationsFactory.test.net.spec.ts new file mode 100644 index 00000000..05f9edea --- /dev/null +++ b/src/tokenOperations/tokenOperationsFactory.test.net.spec.ts @@ -0,0 +1,565 @@ +import { assert } from "chai"; +import { AsyncTimer } from "../asyncTimer"; +import { GasEstimator } from "../gasEstimator"; +import { INetworkConfig, ITransactionOnNetwork } from "../interfaceOfNetwork"; +import { loadTestWallets, TestWallet } from "../testutils"; +import { createTestnetProvider, INetworkProvider } from "../testutils/networkProviders"; +import { TokenPayment } from "../tokenPayment"; +import { Transaction } from "../transaction"; +import { TransactionWatcher } from "../transactionWatcher"; +import { TransfersFactory } from "../transfersFactory"; +import { TokenOperationsFactory } from "./tokenOperationsFactory"; +import { TokenOperationsFactoryConfig } from "./tokenOperationsFactoryConfig"; +import { TokenOperationsOutcomeParser } from "./tokenOperationsOutcomeParser"; + +describe("test factory on testnet", function () { + let frank: TestWallet, grace: TestWallet; + let provider: INetworkProvider; + let watcher: TransactionWatcher; + let network: INetworkConfig; + let factory: TokenOperationsFactory; + let parser: TokenOperationsOutcomeParser; + let transfersFactory: TransfersFactory; + + before(async function () { + console.log(`> ${this.currentTest?.title} ...`); + + ({ frank, grace } = await loadTestWallets()); + + provider = createTestnetProvider(); + watcher = new TransactionWatcher(provider); + network = await provider.getNetworkConfig(); + factory = new TokenOperationsFactory(new TokenOperationsFactoryConfig(network.ChainID)); + parser = new TokenOperationsOutcomeParser(); + transfersFactory = new TransfersFactory(new GasEstimator()); + }); + + it("should issue fungible, mint, burn", async function () { + this.timeout(120000); + await frank.sync(provider); + await grace.sync(provider); + + // Issue + const tx1 = factory.issueFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + initialSupply: 100, + numDecimals: 0, + canFreeze: true, + canWipe: true, + canPause: true, + canMint: true, + canBurn: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Set roles (give Grace the ability to mint and burn) + const tx2 = factory.setSpecialRoleOnFungible({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tx1Outcome.tokenIdentifier, + addRoleLocalMint: true, + addRoleLocalBurn: true, + transactionNonce: frank.account.nonce + }); + + const tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + const tx2Outcome = parser.parseSetSpecialRole(tx2OnNetwork); + assert.include(tx2Outcome.roles, "ESDTRoleLocalMint"); + assert.include(tx2Outcome.roles, "ESDTRoleLocalBurn"); + + // Mint (Grace mints for herself) + const tx3 = factory.localMint({ + manager: grace.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + supplyToMint: 200, + transactionNonce: grace.account.nonce + }); + + const tx3OnNetwork = await processTransaction(grace, tx3, "tx3"); + const tx3Outcome = parser.parseLocalMint(tx3OnNetwork); + assert.equal(tx3Outcome.mintedSupply, "200"); + + // Burn (Grace burns 50 of her tokens) + const tx4 = factory.localBurn({ + manager: grace.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + supplyToBurn: 50, + transactionNonce: grace.account.nonce + }); + + const tx4OnNetwork = await processTransaction(grace, tx4, "tx4"); + const tx4Outcome = parser.parseLocalBurn(tx4OnNetwork); + assert.equal(tx4Outcome.burntSupply, "50"); + }); + + it("should issue fungible, pause, unpause", async function () { + this.timeout(240000); + await frank.sync(provider); + + // Issue + const tx1 = factory.issueFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + initialSupply: 100, + numDecimals: 0, + canFreeze: true, + canWipe: true, + canPause: true, + canMint: true, + canBurn: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Pause + const tx2 = factory.pause({ + manager: frank.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + const _tx2Outcome = parser.parsePause(tx2OnNetwork); + + // Unpause + const tx3 = factory.unpause({ + manager: frank.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx3OnNetwork = await processTransaction(frank, tx3, "tx3"); + const _tx3Outcome = parser.parseUnpause(tx3OnNetwork); + + // Send some tokens to Grace + const tx4 = transfersFactory.createESDTTransfer({ + payment: TokenPayment.fungibleFromBigInteger(tokenIdentifier, 10), + sender: frank.account.address, + receiver: grace.account.address, + chainID: network.ChainID, + nonce: frank.account.nonce + }); + + const _tx4OnNetwork = await processTransaction(frank, tx4, "tx4"); + }); + + it("should issue fungible, freeze, unfreeze", async function () { + this.timeout(240000); + await frank.sync(provider); + + // Issue + const tx1 = factory.issueFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + initialSupply: 100, + numDecimals: 0, + canFreeze: true, + canWipe: true, + canPause: true, + canMint: true, + canBurn: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Send some tokens to Grace + const tx2 = transfersFactory.createESDTTransfer({ + payment: TokenPayment.fungibleFromBigInteger(tokenIdentifier, 10), + sender: frank.account.address, + receiver: grace.account.address, + chainID: network.ChainID, + nonce: frank.account.nonce + }); + + const _tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + + // Freeze + const tx3 = factory.freeze({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx3OnNetwork = await processTransaction(frank, tx3, "tx3"); + const tx3Outcome = parser.parseFreeze(tx3OnNetwork); + assert.equal(tx3Outcome.userAddress, grace.address.bech32()); + assert.equal(tx3Outcome.tokenIdentifier, tokenIdentifier); + assert.equal(tx3Outcome.nonce, "0"); + assert.equal(tx3Outcome.balance, "10"); + + // Unfreeze + const tx4 = factory.unfreeze({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx4OnNetwork = await processTransaction(frank, tx4, "tx4"); + const tx4Outcome = parser.parseUnfreeze(tx4OnNetwork); + assert.equal(tx4Outcome.userAddress, grace.address.bech32()); + assert.equal(tx4Outcome.tokenIdentifier, tokenIdentifier); + assert.equal(tx4Outcome.nonce, "0"); + assert.equal(tx4Outcome.balance, "10"); + }); + + it("should issue fungible, freeze, wipe", async function () { + this.timeout(240000); + await frank.sync(provider); + + // Issue + const tx1 = factory.issueFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + initialSupply: 100, + numDecimals: 0, + canFreeze: true, + canWipe: true, + canPause: true, + canMint: true, + canBurn: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Send some tokens to Grace + const tx2 = transfersFactory.createESDTTransfer({ + payment: TokenPayment.fungibleFromBigInteger(tokenIdentifier, 10), + sender: frank.account.address, + receiver: grace.account.address, + chainID: network.ChainID, + nonce: frank.account.nonce + }); + + const _tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + + // Freeze + const tx3 = factory.freeze({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx3OnNetwork = await processTransaction(frank, tx3, "tx3"); + const tx3Outcome = parser.parseFreeze(tx3OnNetwork); + assert.equal(tx3Outcome.userAddress, grace.address.bech32()); + assert.equal(tx3Outcome.tokenIdentifier, tokenIdentifier); + assert.equal(tx3Outcome.nonce, "0"); + assert.equal(tx3Outcome.balance, "10"); + + // Wipe + const tx4 = factory.wipe({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + transactionNonce: frank.account.nonce + }); + + const tx4OnNetwork = await processTransaction(frank, tx4, "tx4"); + const tx4Outcome = parser.parseWipe(tx4OnNetwork); + assert.equal(tx4Outcome.userAddress, grace.address.bech32()); + assert.equal(tx4Outcome.tokenIdentifier, tokenIdentifier); + assert.equal(tx4Outcome.nonce, "0"); + assert.equal(tx4Outcome.balance, "10"); + }); + + it("should issue and create NFT, then update attributes", async function () { + this.timeout(180000); + await frank.sync(provider); + await grace.sync(provider); + + // Issue NFT + const tx1 = factory.issueNonFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueNonFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Set roles (give Grace the ability to create NFTs) + const tx2 = factory.setSpecialRoleOnNonFungible({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + addRoleNFTCreate: true, + addRoleNFTBurn: false, + addRoleNFTUpdateAttributes: true, + addRoleNFTAddURI: true, + addRoleESDTTransferRole: false, + transactionNonce: frank.account.nonce + }); + + const tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + const tx2Outcome = parser.parseSetSpecialRole(tx2OnNetwork); + assert.include(tx2Outcome.roles, "ESDTRoleNFTCreate"); + assert.include(tx2Outcome.roles, "ESDTRoleNFTUpdateAttributes"); + + // Create NFTs, then update their attributes + for (let i = 1; i <= 2; i++) { + // Create + const txCreate = factory.nftCreate({ + creator: grace.address, + tokenIdentifier: tokenIdentifier, + initialQuantity: 1, + name: `test-${i}`, + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + transactionNonce: grace.account.nonce + }); + + const txCreateOnNetwork = await processTransaction(grace, txCreate, "txCreate"); + const txCreateOutcome = parser.parseNFTCreate(txCreateOnNetwork); + + assert.equal(txCreateOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txCreateOutcome.nonce, i.toString()); + assert.equal(txCreateOutcome.initialQuantity, "1"); + + // Update attributes + const txUpdate = factory.updateAttributes({ + manager: grace.address, + tokenIdentifier: txCreateOutcome.tokenIdentifier, + tokenNonce: txCreateOutcome.nonce, + attributes: Buffer.from("updated"), + transactionNonce: grace.account.nonce, + }); + + const txUpdateOnNetwork = await processTransaction(grace, txUpdate, "txUpdate"); + const txUpdateOutcome = parser.parseUpdateAttributes(txUpdateOnNetwork); + + assert.equal(txUpdateOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txUpdateOutcome.nonce, i.toString()); + assert.deepEqual(txUpdateOutcome.attributes, Buffer.from("updated")); + } + }); + + it("should issue and create SFT, add quantity, burn quantity", async function () { + this.timeout(200000); + await frank.sync(provider); + await grace.sync(provider); + + // Issue SFT + const tx1 = factory.issueSemiFungible({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseIssueSemiFungible(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Set roles (give Grace the ability to create SFTs) + const tx2 = factory.setSpecialRoleOnSemiFungible({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + addRoleNFTCreate: true, + addRoleNFTBurn: false, + addRoleNFTAddQuantity: true, + addRoleESDTTransferRole: false, + transactionNonce: frank.account.nonce + }); + + const tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + const tx2Outcome = parser.parseSetSpecialRole(tx2OnNetwork); + assert.include(tx2Outcome.roles, "ESDTRoleNFTCreate"); + assert.include(tx2Outcome.roles, "ESDTRoleNFTAddQuantity"); + + for (let i = 1; i <= 2; i++) { + // Create SFT + const txCreate = factory.nftCreate({ + creator: grace.address, + tokenIdentifier: tokenIdentifier, + initialQuantity: i * 10, + name: `test-${i}`, + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + transactionNonce: grace.account.nonce + }); + + const txCreateOnNetwork = await processTransaction(grace, txCreate, "txCreate"); + const txCreateOutcome = parser.parseNFTCreate(txCreateOnNetwork); + + assert.equal(txCreateOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txCreateOutcome.nonce, i.toString()); + assert.equal(txCreateOutcome.initialQuantity, (i * 10).toString()); + + // Add quantity + const txAddQuantity = factory.addQuantity({ + manager: grace.address, + tokenIdentifier: txCreateOutcome.tokenIdentifier, + tokenNonce: txCreateOutcome.nonce, + quantityToAdd: "3", + transactionNonce: grace.account.nonce + }); + + const txAddQuantityOnNetwork = await processTransaction(grace, txAddQuantity, "txAddQuantity"); + const txAddQuantityOutcome = parser.parseAddQuantity(txAddQuantityOnNetwork); + + assert.equal(txAddQuantityOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txAddQuantityOutcome.nonce, i.toString()); + assert.equal(txAddQuantityOutcome.addedQuantity, "3"); + + // Burn quantity + const txBurnQuantity = factory.burnQuantity({ + manager: grace.address, + tokenIdentifier: txCreateOutcome.tokenIdentifier, + tokenNonce: txCreateOutcome.nonce, + quantityToBurn: "2", + transactionNonce: grace.account.nonce + }); + + const txBurnQuantityOnNetwork = await processTransaction(grace, txBurnQuantity, "txBurnQuantity"); + const txBurnQuantityOutcome = parser.parseBurnQuantity(txBurnQuantityOnNetwork); + + assert.equal(txBurnQuantityOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txBurnQuantityOutcome.nonce, i.toString()); + assert.equal(txBurnQuantityOutcome.burntQuantity, "2"); + } + }); + + it("should register and create Meta ESDT", async function () { + this.timeout(180000); + await frank.sync(provider); + await grace.sync(provider); + + // Register Meta ESDT + const tx1 = factory.registerMetaESDT({ + issuer: frank.address, + tokenName: "FRANK", + tokenTicker: "FRANK", + numDecimals: 10, + canFreeze: true, + canWipe: true, + canPause: true, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + transactionNonce: frank.account.nonce + }); + + const tx1OnNetwork = await processTransaction(frank, tx1, "tx1"); + const tx1Outcome = parser.parseRegisterMetaESDT(tx1OnNetwork); + const tokenIdentifier = tx1Outcome.tokenIdentifier; + assert.isTrue(tokenIdentifier.includes("FRANK")); + + // Set roles (give Grace the ability to create Meta ESDTs) + const tx2 = factory.setSpecialRoleOnMetaESDT({ + manager: frank.address, + user: grace.address, + tokenIdentifier: tokenIdentifier, + addRoleNFTCreate: true, + addRoleNFTBurn: false, + addRoleNFTAddQuantity: true, + addRoleESDTTransferRole: false, + transactionNonce: frank.account.nonce + }); + + const tx2OnNetwork = await processTransaction(frank, tx2, "tx2"); + const tx2Outcome = parser.parseSetSpecialRole(tx2OnNetwork); + assert.include(tx2Outcome.roles, "ESDTRoleNFTCreate"); + assert.include(tx2Outcome.roles, "ESDTRoleNFTAddQuantity"); + + // Create tokens + for (let i = 1; i <= 3; i++) { + const tx = factory.nftCreate({ + creator: grace.address, + tokenIdentifier: tokenIdentifier, + initialQuantity: i * 10, + name: `test-${i}`, + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + transactionNonce: grace.account.nonce + }); + + const txOnNetwork = await processTransaction(grace, tx, "tx"); + const txOutcome = parser.parseNFTCreate(txOnNetwork); + + assert.equal(txOutcome.tokenIdentifier, tokenIdentifier); + assert.equal(txOutcome.nonce, i.toString()); + assert.equal(txOutcome.initialQuantity, (i * 10).toString()); + } + }); + + async function processTransaction(wallet: TestWallet, transaction: Transaction, tag: string): Promise { + wallet.account.incrementNonce(); + await wallet.signer.sign(transaction); + await provider.sendTransaction(transaction); + console.log(`Sent transaction [${tag}]: ${transaction.getHash().hex()}`); + + let transactionOnNetwork = await watcher.awaitCompleted(transaction); + + // For some transactions, the "isCompleted" field is somehow incorrect (false positive). + // Let's wait a bit more to have the outcome. + await (new AsyncTimer("test")).start(1000); + + transactionOnNetwork = await watcher.awaitCompleted(transaction); + return transactionOnNetwork; + } +}); diff --git a/src/tokenOperations/tokenOperationsFactory.ts b/src/tokenOperations/tokenOperationsFactory.ts new file mode 100644 index 00000000..22b1e8ca --- /dev/null +++ b/src/tokenOperations/tokenOperationsFactory.ts @@ -0,0 +1,597 @@ +import BigNumber from "bignumber.js"; +import { ARGUMENTS_SEPARATOR, TRANSACTION_OPTIONS_DEFAULT, TRANSACTION_VERSION_DEFAULT } from "../constants"; +import { IAddress, IChainID, IGasLimit, IGasPrice, INonce, ITransactionValue } from "../interface"; +import { TransactionOptions, TransactionVersion } from "../networkParams"; +import { Transaction } from "../transaction"; +import { TransactionPayload } from "../transactionPayload"; +import { addressToHex, bigIntToHex, bufferToHex, utf8ToHex } from "./codec"; + +interface IConfig { + chainID: IChainID; + minGasPrice: IGasPrice; + minGasLimit: IGasLimit; + gasLimitPerByte: IGasLimit; + gasLimitIssue: IGasLimit; + gasLimitESDTLocalMint: IGasLimit; + gasLimitESDTLocalBurn: IGasLimit; + gasLimitSetSpecialRole: IGasLimit; + gasLimitPausing: IGasLimit; + gasLimitFreezing: IGasLimit; + gasLimitWiping: IGasLimit; + gasLimitESDTNFTCreate: IGasLimit; + gasLimitESDTNFTUpdateAttributes: IGasLimit; + gasLimitESDTNFTAddQuantity: IGasLimit; + gasLimitESDTNFTBurn: IGasLimit; + gasLimitStorePerByte: IGasLimit; + issueCost: BigNumber.Value; + esdtContractAddress: IAddress; +} + +interface IBaseArgs { + transactionNonce?: INonce; + value?: ITransactionValue; + gasPrice?: IGasPrice; + gasLimit?: IGasLimit; +} + +interface IIssueFungibleArgs extends IBaseArgs { + issuer: IAddress; + tokenName: string; + tokenTicker: string; + initialSupply: number; + numDecimals: number; + canFreeze: boolean; + canWipe: boolean; + canPause: boolean; + canMint: boolean; + canBurn: boolean; + canChangeOwner: boolean; + canUpgrade: boolean; + canAddSpecialRoles: boolean; +} + +interface IIssueSemiFungibleArgs extends IBaseArgs { + issuer: IAddress; + tokenName: string; + tokenTicker: string; + canFreeze: boolean; + canWipe: boolean; + canPause: boolean; + canTransferNFTCreateRole: boolean; + canChangeOwner: boolean; + canUpgrade: boolean; + canAddSpecialRoles: boolean; +} + +interface IIssueNonFungibleArgs extends IIssueSemiFungibleArgs { +} + +interface IRegisterMetaESDT extends IIssueSemiFungibleArgs { + numDecimals: number; +} + +interface IFungibleSetSpecialRoleArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; + addRoleLocalMint: boolean; + addRoleLocalBurn: boolean; +} + +interface ISemiFungibleSetSpecialRoleArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; + addRoleNFTCreate: boolean; + addRoleNFTBurn: boolean; + addRoleNFTAddQuantity: boolean; + addRoleESDTTransferRole: boolean; +} + +interface INonFungibleSetSpecialRoleArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; + addRoleNFTCreate: boolean; + addRoleNFTBurn: boolean; + addRoleNFTUpdateAttributes: boolean; + addRoleNFTAddURI: boolean; + addRoleESDTTransferRole: boolean; +} + +interface INFTCreateArgs extends IBaseArgs { + creator: IAddress; + tokenIdentifier: string; + initialQuantity: number; + name: string; + royalties: number; + hash: string; + attributes: Buffer; + uris: string[]; +} + +interface IPausingArgs extends IBaseArgs { + manager: IAddress; + tokenIdentifier: string; +} + +interface IFreezingArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; +} + +interface IWipingArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; +} + +interface ILocalMintArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; + supplyToMint: BigNumber.Value +} + +interface ILocalBurnArgs extends IBaseArgs { + manager: IAddress; + user: IAddress; + tokenIdentifier: string; + supplyToBurn: BigNumber.Value +} + +interface IUpdateAttributesArgs extends IBaseArgs { + manager: IAddress; + tokenIdentifier: string; + tokenNonce: BigNumber.Value; + attributes: Buffer; +} + +interface IAddQuantityArgs extends IBaseArgs { + manager: IAddress; + tokenIdentifier: string; + tokenNonce: BigNumber.Value; + quantityToAdd: BigNumber.Value +} + +interface IBurnQuantityArgs extends IBaseArgs { + manager: IAddress; + tokenIdentifier: string; + tokenNonce: BigNumber.Value; + quantityToBurn: BigNumber.Value +} + +export class TokenOperationsFactory { + private readonly config: IConfig; + private readonly trueAsHex; + + constructor(config: IConfig) { + this.config = config; + this.trueAsHex = utf8ToHex("true"); + } + + issueFungible(args: IIssueFungibleArgs): Transaction { + const parts = [ + "issue", + utf8ToHex(args.tokenName), + utf8ToHex(args.tokenTicker), + bigIntToHex(args.initialSupply), + bigIntToHex(args.numDecimals), + ...(args.canFreeze ? [utf8ToHex("canFreeze"), this.trueAsHex] : []), + ...(args.canWipe ? [utf8ToHex("canWipe"), this.trueAsHex] : []), + ...(args.canPause ? [utf8ToHex("canPause"), this.trueAsHex] : []), + ...(args.canMint ? [utf8ToHex("canMint"), this.trueAsHex] : []), + ...(args.canBurn ? [utf8ToHex("canBurn"), this.trueAsHex] : []), + ...(args.canChangeOwner ? [utf8ToHex("canChangeOwner"), this.trueAsHex] : []), + ...(args.canUpgrade ? [utf8ToHex("canUpgrade"), this.trueAsHex] : []), + ...(args.canAddSpecialRoles ? [utf8ToHex("canAddSpecialRoles"), this.trueAsHex] : []), + ]; + + return this.createTransaction({ + sender: args.issuer, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + value: this.config.issueCost, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitIssue, + dataParts: parts + }); + } + + issueSemiFungible(args: IIssueSemiFungibleArgs): Transaction { + const parts = [ + "issueSemiFungible", + utf8ToHex(args.tokenName), + utf8ToHex(args.tokenTicker), + ...(args.canFreeze ? [utf8ToHex("canFreeze"), this.trueAsHex] : []), + ...(args.canWipe ? [utf8ToHex("canWipe"), this.trueAsHex] : []), + ...(args.canPause ? [utf8ToHex("canPause"), this.trueAsHex] : []), + ...(args.canTransferNFTCreateRole ? [utf8ToHex("canTransferNFTCreateRole"), this.trueAsHex] : []), + ...(args.canChangeOwner ? [utf8ToHex("canChangeOwner"), this.trueAsHex] : []), + ...(args.canUpgrade ? [utf8ToHex("canUpgrade"), this.trueAsHex] : []), + ...(args.canAddSpecialRoles ? [utf8ToHex("canAddSpecialRoles"), this.trueAsHex] : []), + ]; + + return this.createTransaction({ + sender: args.issuer, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + value: this.config.issueCost, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitIssue, + dataParts: parts + }); + } + + issueNonFungible(args: IIssueNonFungibleArgs): Transaction { + const parts = [ + "issueNonFungible", + utf8ToHex(args.tokenName), + utf8ToHex(args.tokenTicker), + ...(args.canFreeze ? [utf8ToHex("canFreeze"), this.trueAsHex] : []), + ...(args.canWipe ? [utf8ToHex("canWipe"), this.trueAsHex] : []), + ...(args.canPause ? [utf8ToHex("canPause"), this.trueAsHex] : []), + ...(args.canTransferNFTCreateRole ? [utf8ToHex("canTransferNFTCreateRole"), this.trueAsHex] : []), + ...(args.canChangeOwner ? [utf8ToHex("canChangeOwner"), this.trueAsHex] : []), + ...(args.canUpgrade ? [utf8ToHex("canUpgrade"), this.trueAsHex] : []), + ...(args.canAddSpecialRoles ? [utf8ToHex("canAddSpecialRoles"), this.trueAsHex] : []), + ]; + + return this.createTransaction({ + sender: args.issuer, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + value: this.config.issueCost, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitIssue, + dataParts: parts + }); + } + + registerMetaESDT(args: IRegisterMetaESDT): Transaction { + const parts = [ + "registerMetaESDT", + utf8ToHex(args.tokenName), + utf8ToHex(args.tokenTicker), + bigIntToHex(args.numDecimals), + ...(args.canFreeze ? [utf8ToHex("canFreeze"), this.trueAsHex] : []), + ...(args.canWipe ? [utf8ToHex("canWipe"), this.trueAsHex] : []), + ...(args.canPause ? [utf8ToHex("canPause"), this.trueAsHex] : []), + ...(args.canTransferNFTCreateRole ? [utf8ToHex("canTransferNFTCreateRole"), this.trueAsHex] : []), + ...(args.canChangeOwner ? [utf8ToHex("canChangeOwner"), this.trueAsHex] : []), + ...(args.canUpgrade ? [utf8ToHex("canUpgrade"), this.trueAsHex] : []), + ...(args.canAddSpecialRoles ? [utf8ToHex("canAddSpecialRoles"), this.trueAsHex] : []), + ]; + + return this.createTransaction({ + sender: args.issuer, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + value: this.config.issueCost, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitIssue, + dataParts: parts + }); + } + + setSpecialRoleOnFungible(args: IFungibleSetSpecialRoleArgs): Transaction { + const parts = [ + "setSpecialRole", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user), + ...(args.addRoleLocalMint ? [utf8ToHex("ESDTRoleLocalMint")] : []), + ...(args.addRoleLocalBurn ? [utf8ToHex("ESDTRoleLocalBurn")] : []), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitSetSpecialRole, + dataParts: parts + }); + } + + setSpecialRoleOnSemiFungible(args: ISemiFungibleSetSpecialRoleArgs): Transaction { + const parts = [ + "setSpecialRole", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user), + ...(args.addRoleNFTCreate ? [utf8ToHex("ESDTRoleNFTCreate")] : []), + ...(args.addRoleNFTBurn ? [utf8ToHex("ESDTRoleNFTBurn")] : []), + ...(args.addRoleNFTAddQuantity ? [utf8ToHex("ESDTRoleNFTAddQuantity")] : []), + ...(args.addRoleESDTTransferRole ? [utf8ToHex("ESDTTransferRole")] : []), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitSetSpecialRole, + dataParts: parts + }); + } + + setSpecialRoleOnMetaESDT(args: ISemiFungibleSetSpecialRoleArgs): Transaction { + return this.setSpecialRoleOnSemiFungible(args); + } + + setSpecialRoleOnNonFungible(args: INonFungibleSetSpecialRoleArgs): Transaction { + const parts = [ + "setSpecialRole", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user), + ...(args.addRoleNFTCreate ? [utf8ToHex("ESDTRoleNFTCreate")] : []), + ...(args.addRoleNFTBurn ? [utf8ToHex("ESDTRoleNFTBurn")] : []), + ...(args.addRoleNFTUpdateAttributes ? [utf8ToHex("ESDTRoleNFTUpdateAttributes")] : []), + ...(args.addRoleNFTAddURI ? [utf8ToHex("ESDTRoleNFTAddURI")] : []), + ...(args.addRoleESDTTransferRole ? [utf8ToHex("ESDTTransferRole")] : []), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitSetSpecialRole, + dataParts: parts + }); + } + + nftCreate(args: INFTCreateArgs): Transaction { + const parts = [ + "ESDTNFTCreate", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.initialQuantity), + utf8ToHex(args.name), + bigIntToHex(args.royalties), + utf8ToHex(args.hash), + bufferToHex(args.attributes), + ...args.uris.map(utf8ToHex), + ]; + + // Note that the following is an approximation (a reasonable one): + const nftData = args.name + args.hash + args.attributes + args.uris.join(""); + const storageGasLimit = nftData.length * this.config.gasLimitStorePerByte.valueOf(); + + return this.createTransaction({ + sender: args.creator, + receiver: args.creator, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTNFTCreate.valueOf() + storageGasLimit.valueOf(), + dataParts: parts + }); + } + + pause(args: IPausingArgs): Transaction { + const parts = [ + "pause", + utf8ToHex(args.tokenIdentifier) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitPausing, + dataParts: parts + }); + } + + unpause(args: IPausingArgs): Transaction { + const parts = [ + "unPause", + utf8ToHex(args.tokenIdentifier) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitPausing, + dataParts: parts + }); + } + + freeze(args: IFreezingArgs): Transaction { + const parts = [ + "freeze", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitFreezing, + dataParts: parts + }); + } + + unfreeze(args: IFreezingArgs): Transaction { + const parts = [ + "unFreeze", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitFreezing, + dataParts: parts + }); + } + + wipe(args: IWipingArgs): Transaction { + const parts = [ + "wipe", + utf8ToHex(args.tokenIdentifier), + addressToHex(args.user) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: this.config.esdtContractAddress, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitWiping, + dataParts: parts + }); + } + + localMint(args: ILocalMintArgs): Transaction { + const parts = [ + "ESDTLocalMint", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.supplyToMint), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: args.manager, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTLocalMint, + dataParts: parts + }); + } + + localBurn(args: ILocalBurnArgs): Transaction { + const parts = [ + "ESDTLocalBurn", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.supplyToBurn), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: args.manager, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTLocalBurn, + dataParts: parts + }); + } + + updateAttributes(args: IUpdateAttributesArgs): Transaction { + const parts = [ + "ESDTNFTUpdateAttributes", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.tokenNonce), + bufferToHex(args.attributes), + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: args.manager, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTNFTUpdateAttributes, + dataParts: parts + }); + } + + addQuantity(args: IAddQuantityArgs): Transaction { + const parts = [ + "ESDTNFTAddQuantity", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.tokenNonce), + bigIntToHex(args.quantityToAdd) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: args.manager, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTNFTAddQuantity, + dataParts: parts + }); + } + + burnQuantity(args: IBurnQuantityArgs): Transaction { + const parts = [ + "ESDTNFTBurn", + utf8ToHex(args.tokenIdentifier), + bigIntToHex(args.tokenNonce), + bigIntToHex(args.quantityToBurn) + ]; + + return this.createTransaction({ + sender: args.manager, + receiver: args.manager, + nonce: args.transactionNonce, + gasPrice: args.gasPrice, + gasLimitHint: args.gasLimit, + executionGasLimit: this.config.gasLimitESDTNFTBurn, + dataParts: parts + }); + } + + private createTransaction({ sender, receiver, nonce, value, gasPrice, gasLimitHint, executionGasLimit, dataParts }: { + sender: IAddress; + receiver: IAddress; + nonce?: INonce; + value?: ITransactionValue; + gasPrice?: IGasPrice; + gasLimitHint?: IGasLimit; + executionGasLimit: IGasLimit; + dataParts: string[]; + }): Transaction { + const payload = this.buildTransactionPayload(dataParts); + const gasLimit = gasLimitHint || this.computeGasLimit(payload, executionGasLimit); + const version = new TransactionVersion(TRANSACTION_VERSION_DEFAULT); + const options = new TransactionOptions(TRANSACTION_OPTIONS_DEFAULT); + + return new Transaction({ + chainID: this.config.chainID, + sender: sender, + receiver: receiver, + gasLimit: gasLimit, + gasPrice: gasPrice, + nonce: nonce || 0, + value: value || 0, + data: payload, + version: version, + options: options + }); + } + + private buildTransactionPayload(parts: string[]): TransactionPayload { + const data = parts.join(ARGUMENTS_SEPARATOR); + return new TransactionPayload(data); + } + + private computeGasLimit(payload: TransactionPayload, executionGas: IGasLimit): IGasLimit { + const dataMovementGas = this.config.minGasLimit.valueOf() + this.config.gasLimitPerByte.valueOf() * payload.length(); + return dataMovementGas + executionGas.valueOf(); + } +} diff --git a/src/tokenOperations/tokenOperationsFactoryConfig.ts b/src/tokenOperations/tokenOperationsFactoryConfig.ts new file mode 100644 index 00000000..e7be85e5 --- /dev/null +++ b/src/tokenOperations/tokenOperationsFactoryConfig.ts @@ -0,0 +1,28 @@ +import BigNumber from "bignumber.js"; +import { Address } from "../address"; +import { IAddress, IChainID, IGasLimit, IGasPrice } from "../interface"; + +export class TokenOperationsFactoryConfig { + chainID: IChainID; + minGasPrice: IGasPrice = 1000000000; + minGasLimit = 50000; + gasLimitPerByte = 1500; + gasLimitIssue: IGasLimit = 60000000; + gasLimitESDTLocalMint: IGasLimit = 300000; + gasLimitESDTLocalBurn: IGasLimit = 300000; + gasLimitSetSpecialRole: IGasLimit = 60000000; + gasLimitPausing: IGasLimit = 60000000; + gasLimitFreezing: IGasLimit = 60000000; + gasLimitWiping: IGasLimit = 60000000; + gasLimitESDTNFTCreate: IGasLimit = 3000000; + gasLimitESDTNFTUpdateAttributes: IGasLimit = 1000000; + gasLimitESDTNFTAddQuantity: IGasLimit = 1000000; + gasLimitESDTNFTBurn: IGasLimit = 1000000; + gasLimitStorePerByte: IGasLimit = 50000; + issueCost: BigNumber.Value = "50000000000000000"; + esdtContractAddress: IAddress = Address.fromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + + constructor(chainID: IChainID) { + this.chainID = chainID; + } +} diff --git a/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts b/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts new file mode 100644 index 00000000..edabb2ae --- /dev/null +++ b/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts @@ -0,0 +1,116 @@ +import { assert } from "chai"; +import { loadTestWallets, TestWallet } from "../testutils"; +import { bigIntToBuffer } from "./codec"; +import { TokenOperationsOutcomeParser } from "./tokenOperationsOutcomeParser"; + +describe("test parsers", () => { + const parser = new TokenOperationsOutcomeParser(); + let frank: TestWallet, grace: TestWallet; + + before(async function () { + ({ frank, grace } = await loadTestWallets()); + }); + + it("should parse outcome of issueFungible", () => { + const outcome = parser.parseIssueFungible({ + hash: "hash", + contractResults: { items: [] }, + logs: { + events: [ + { + address: frank.address, + identifier: "issue", + topics: [createTopic(Buffer.from("FOOBAR"))], + data: "" + } + ] + } + }); + + assert.equal(outcome.tokenIdentifier, "FOOBAR"); + }); + + it("should parse outcome of setSpecialRole", () => { + const outcome = parser.parseSetSpecialRole({ + hash: "hash", + contractResults: { items: [] }, + logs: { + events: [ + { + address: grace.address, + identifier: "ESDTSetRole", + topics: [ + createTopic(Buffer.from("FOOBAR")), + createTopic(Buffer.from("")), + createTopic(Buffer.from("")), + createTopic(Buffer.from("ESDTRoleLocalMint")), + createTopic(Buffer.from("ESDTRoleLocalBurn")) + ], + data: "" + } + ] + } + }); + + assert.equal(outcome.tokenIdentifier, "FOOBAR"); + assert.deepEqual(outcome.roles, ["ESDTRoleLocalMint", "ESDTRoleLocalBurn"]); + assert.equal(outcome.userAddress, grace.address.toString()); + }); + + it("should parse outcome of localMint", () => { + const outcome = parser.parseLocalMint({ + hash: "hash", + contractResults: { items: [] }, + logs: { + events: [ + { + address: grace.address, + identifier: "ESDTLocalMint", + topics: [ + createTopic(Buffer.from("FOOBAR")), + createTopic(Buffer.from("")), + createTopic(bigIntToBuffer("200")), + ], + data: "" + } + ] + } + }); + + assert.equal(outcome.tokenIdentifier, "FOOBAR"); + assert.equal(outcome.nonce, "0"); + assert.equal(outcome.mintedSupply, "200"); + assert.equal(outcome.userAddress, grace.address.toString()); + }); + + it("should parse outcome of nftCreate", () => { + const outcome = parser.parseNFTCreate({ + hash: "hash", + contractResults: { items: [] }, + logs: { + events: [ + { + address: grace.address, + identifier: "ESDTNFTCreate", + topics: [ + createTopic(Buffer.from("FOOBAR")), + createTopic(bigIntToBuffer("42")), + createTopic(bigIntToBuffer("1")), + ], + data: "" + } + ] + } + }); + + assert.equal(outcome.tokenIdentifier, "FOOBAR"); + assert.equal(outcome.nonce, "42"); + assert.equal(outcome.initialQuantity, "1"); + }); + + function createTopic(value: Buffer): any { + return { + valueOf: () => value + }; + } +}); diff --git a/src/tokenOperations/tokenOperationsOutcomeParser.ts b/src/tokenOperations/tokenOperationsOutcomeParser.ts new file mode 100644 index 00000000..c988bb24 --- /dev/null +++ b/src/tokenOperations/tokenOperationsOutcomeParser.ts @@ -0,0 +1,303 @@ +import { Address } from "../address"; +import { ErrCannotParseTransactionOutcome } from "../errors"; +import { IAddress } from "../interface"; +import { bufferToBigInt } from "./codec"; + + +interface ITransactionOnNetwork { + hash: string; + contractResults: IContractResults; + logs: ITransactionLogs; +} + +interface IContractResults { + items: IContractResultItem[]; +} + +interface IContractResultItem { + logs: ITransactionLogs; +} + +interface ITransactionLogs { + events: ITransactionEvent[]; +} + +interface ITransactionEvent { + readonly address: IAddress; + readonly identifier: string; + readonly topics: ITransactionEventTopic[]; + readonly data: string; +} + +interface ITransactionEventTopic { + valueOf(): any; +} + +export interface IESDTIssueOutcome { + tokenIdentifier: string; +} + +export interface ISetSpecialRoleOutcome { + userAddress: string; + tokenIdentifier: string; + roles: string[]; +} + +export interface INFTCreateOutcome { + tokenIdentifier: string; + nonce: string; + initialQuantity: string; +} + +export interface IMintOutcome { + userAddress: string; + tokenIdentifier: string; + nonce: string; + mintedSupply: string; +} + +export interface IBurnOutcome { + userAddress: string; + tokenIdentifier: string; + nonce: string; + burntSupply: string; +} + +export interface IPausingOutcome { +} + +export interface IFreezingOutcome { + userAddress: string; + tokenIdentifier: string; + nonce: string; + balance: string; +} + +export interface IWipingOutcome { + userAddress: string; + tokenIdentifier: string; + nonce: string; + balance: string; +} + +export interface IUpdateAttributesOutcome { + tokenIdentifier: string; + nonce: string; + attributes: Buffer; +} + +export interface IAddQuantityOutcome { + tokenIdentifier: string; + nonce: string; + addedQuantity: string; +} + +export interface IBurnQuantityOutcome { + tokenIdentifier: string; + nonce: string; + burntQuantity: string; +} + +export class TokenOperationsOutcomeParser { + parseIssueFungible(transaction: ITransactionOnNetwork): IESDTIssueOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "issue"); + const tokenIdentifier = this.extractTokenIdentifier(event); + return { tokenIdentifier: tokenIdentifier }; + } + + parseIssueNonFungible(transaction: ITransactionOnNetwork): IESDTIssueOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "issueNonFungible"); + const tokenIdentifier = this.extractTokenIdentifier(event); + return { tokenIdentifier: tokenIdentifier }; + } + + parseIssueSemiFungible(transaction: ITransactionOnNetwork): IESDTIssueOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "issueSemiFungible"); + const tokenIdentifier = this.extractTokenIdentifier(event); + return { tokenIdentifier: tokenIdentifier }; + } + + parseRegisterMetaESDT(transaction: ITransactionOnNetwork): IESDTIssueOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "registerMetaESDT"); + const tokenIdentifier = this.extractTokenIdentifier(event); + return { tokenIdentifier: tokenIdentifier }; + } + + parseSetSpecialRole(transaction: ITransactionOnNetwork): ISetSpecialRoleOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTSetRole"); + const userAddress = event.address.toString(); + const tokenIdentifier = this.extractTokenIdentifier(event); + const roles = event.topics.slice(3).map(topic => topic.valueOf().toString()); + return { userAddress, tokenIdentifier, roles }; + } + + parseNFTCreate(transaction: ITransactionOnNetwork): INFTCreateOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTNFTCreate"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const initialQuantity = this.extractAmount(event); + return { tokenIdentifier, nonce, initialQuantity }; + } + + parseLocalMint(transaction: ITransactionOnNetwork): IMintOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTLocalMint"); + const userAddress = event.address.toString(); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const mintedSupply = this.extractAmount(event); + return { userAddress, tokenIdentifier, nonce, mintedSupply }; + } + + parseLocalBurn(transaction: ITransactionOnNetwork): IBurnOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTLocalBurn"); + const userAddress = event.address.toString(); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const burntSupply = this.extractAmount(event); + return { userAddress, tokenIdentifier, nonce, burntSupply }; + } + + parsePause(transaction: ITransactionOnNetwork): IPausingOutcome { + this.ensureNoError(transaction); + const _ = this.findSingleEventByIdentifier(transaction, "ESDTPause"); + return {}; + } + + parseUnpause(transaction: ITransactionOnNetwork): IPausingOutcome { + this.ensureNoError(transaction); + const _ = this.findSingleEventByIdentifier(transaction, "ESDTUnPause"); + return {}; + } + + parseFreeze(transaction: ITransactionOnNetwork): IFreezingOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTFreeze"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const balance = this.extractAmount(event); + const userAddress = this.extractAddress(event); + return { userAddress, tokenIdentifier, nonce, balance }; + } + + parseUnfreeze(transaction: ITransactionOnNetwork): IFreezingOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTUnFreeze"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const balance = this.extractAmount(event); + const userAddress = this.extractAddress(event); + return { userAddress, tokenIdentifier, nonce, balance }; + } + + parseWipe(transaction: ITransactionOnNetwork): IWipingOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTWipe"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const balance = this.extractAmount(event); + const userAddress = this.extractAddress(event); + return { userAddress, tokenIdentifier, nonce, balance }; + } + + parseUpdateAttributes(transaction: ITransactionOnNetwork): IUpdateAttributesOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTNFTUpdateAttributes"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const attributes = event.topics[3]?.valueOf(); + return { tokenIdentifier, nonce, attributes }; + } + + parseAddQuantity(transaction: ITransactionOnNetwork): IAddQuantityOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTNFTAddQuantity"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const addedQuantity = this.extractAmount(event); + return { tokenIdentifier, nonce, addedQuantity }; + } + + parseBurnQuantity(transaction: ITransactionOnNetwork): IBurnQuantityOutcome { + this.ensureNoError(transaction); + + const event = this.findSingleEventByIdentifier(transaction, "ESDTNFTBurn"); + const tokenIdentifier = this.extractTokenIdentifier(event); + const nonce = this.extractNonce(event); + const burntQuantity = this.extractAmount(event); + return { tokenIdentifier, nonce, burntQuantity }; + } + + private ensureNoError(transaction: ITransactionOnNetwork) { + for (const event of transaction.logs.events) { + if (event.identifier == "signalError") { + const data = Buffer.from(event.data.substring(1), "hex").toString(); + const message = event.topics[1]?.valueOf().toString(); + + throw new ErrCannotParseTransactionOutcome(transaction.hash, `encountered signalError: ${message} (${data})`); + } + } + } + + private findSingleEventByIdentifier(transaction: ITransactionOnNetwork, identifier: string): ITransactionEvent { + const events = this.gatherAllEvents(transaction).filter(event => event.identifier == identifier); + + if (events.length == 0) { + throw new ErrCannotParseTransactionOutcome(transaction.hash, `cannot find event of type ${identifier}`); + } + if (events.length > 1) { + throw new ErrCannotParseTransactionOutcome(transaction.hash, `more than one event of type ${identifier}`); + } + + return events[0]; + } + + private gatherAllEvents(transaction: ITransactionOnNetwork): ITransactionEvent[] { + const allEvents = []; + + allEvents.push(...transaction.logs.events); + + for (const item of transaction.contractResults.items) { + allEvents.push(...item.logs.events); + } + + return allEvents; + } + + private extractTokenIdentifier(event: ITransactionEvent): string { + return event.topics[0]?.valueOf().toString(); + } + + private extractNonce(event: ITransactionEvent): string { + return bufferToBigInt(event.topics[1]?.valueOf()).toFixed(0); + } + + private extractAmount(event: ITransactionEvent): string { + return bufferToBigInt(event.topics[2]?.valueOf()).toFixed(0); + } + + private extractAddress(event: ITransactionEvent): string { + return Address.fromBuffer(event.topics[3]?.valueOf()).toString(); + } +} + diff --git a/src/tokenTransferBuilders.ts b/src/tokenTransferBuilders.ts index abc8b26f..114e234b 100644 --- a/src/tokenTransferBuilders.ts +++ b/src/tokenTransferBuilders.ts @@ -5,6 +5,9 @@ import { AddressValue, BigUIntValue, BytesValue, TypedValue, U16Value, U64Value import { TokenPayment } from "./tokenPayment"; import { TransactionPayload } from "./transactionPayload"; +/** + * @deprecated Use {@link TransfersFactory} instead. + */ export class ESDTTransferPayloadBuilder { payment: ITokenPayment = TokenPayment.fungibleFromAmount("", "0", 0); @@ -27,6 +30,9 @@ export class ESDTTransferPayloadBuilder { } } +/** + * @deprecated Use {@link TransfersFactory} instead. + */ export class ESDTNFTTransferPayloadBuilder { payment: ITokenPayment = TokenPayment.nonFungible("", 0); destination: IAddress = new Address(""); @@ -59,6 +65,9 @@ export class ESDTNFTTransferPayloadBuilder { } } +/** + * @deprecated Use {@link TransfersFactory} instead. + */ export class MultiESDTNFTTransferPayloadBuilder { payments: ITokenPayment[] = []; destination: IAddress = new Address(""); diff --git a/src/transactionFactory.spec.ts b/src/transactionFactory.spec.ts index c9223b29..03701246 100644 --- a/src/transactionFactory.spec.ts +++ b/src/transactionFactory.spec.ts @@ -37,21 +37,21 @@ describe("test transaction factory", () => { it("should create ESDT transfers", () => { const transaction = factory.createESDTTransfer({ - payment: TokenPayment.fungibleFromAmount("COUNTER-8b028f", "100.00", 0), + payment: TokenPayment.fungibleFromAmount("TEST-8b028f", "100.00", 2), receiver: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), chainID: "D" }); assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); assert.equal(transaction.getValue(), ""); - assert.equal(transaction.getGasLimit(), 50000 + 44 * 1500 + 200000 + 100000); - assert.equal(transaction.getData().toString(), "ESDTTransfer@434f554e5445522d386230323866@64"); + assert.equal(transaction.getGasLimit(), 50000 + 40 * 1500 + 200000 + 100000); + assert.equal(transaction.getData().toString(), "ESDTTransfer@544553542d386230323866@2710"); assert.equal(transaction.getChainID(), "D"); }); it("should create ESDTNFT transfers", () => { const transaction = factory.createESDTNFTTransfer({ - payment: TokenPayment.nonFungible("ERDJS-38f249", 1), + payment: TokenPayment.nonFungible("TEST-38f249", 1), destination: new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), sender: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), chainID: "D" @@ -60,15 +60,15 @@ describe("test transaction factory", () => { assert.equal(transaction.getSender().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); assert.equal(transaction.getValue(), ""); - assert.equal(transaction.getGasLimit(), 50000 + 111 * 1500 + 200000 + 800000); - assert.equal(transaction.getData().toString(), "ESDTNFTTransfer@4552444a532d333866323439@01@01@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8"); + assert.equal(transaction.getGasLimit(), 50000 + 109 * 1500 + 200000 + 800000); + assert.equal(transaction.getData().toString(), "ESDTNFTTransfer@544553542d333866323439@01@01@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8"); assert.equal(transaction.getChainID(), "D"); }); it("should create Multi ESDTNFT transfers", () => { const transaction = factory.createMultiESDTNFTTransfer({ payments: [ - TokenPayment.nonFungible("ERDJS-38f249", 1), + TokenPayment.nonFungible("FOO-38f249", 1), TokenPayment.fungibleFromAmount("BAR-c80d29", "10.00", 18) ], destination: new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), @@ -79,8 +79,8 @@ describe("test transaction factory", () => { assert.equal(transaction.getSender().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); assert.equal(transaction.getValue(), ""); - assert.equal(transaction.getGasLimit(), 50000 + 158 * 1500 + (200000 + 800000) * 2); - assert.equal(transaction.getData().toString(), "MultiESDTNFTTransfer@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@02@4552444a532d333866323439@01@01@4241522d633830643239@@8ac7230489e80000"); + assert.equal(transaction.getGasLimit(), 50000 + 154 * 1500 + (200000 + 800000) * 2); + assert.equal(transaction.getData().toString(), "MultiESDTNFTTransfer@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@02@464f4f2d333866323439@01@01@4241522d633830643239@@8ac7230489e80000"); assert.equal(transaction.getChainID(), "D"); }); }); diff --git a/src/transactionFactory.ts b/src/transactionFactory.ts index e53446dd..a2fe57f2 100644 --- a/src/transactionFactory.ts +++ b/src/transactionFactory.ts @@ -1,7 +1,7 @@ +import { Address } from "./address"; import { IAddress, IChainID, IGasLimit, IGasPrice, INonce, ITokenPayment, ITransactionPayload, ITransactionValue } from "./interface"; import { ESDTNFTTransferPayloadBuilder, ESDTTransferPayloadBuilder, MultiESDTNFTTransferPayloadBuilder } from "./tokenTransferBuilders"; import { Transaction } from "./transaction"; -import {Address} from "./address"; interface IGasEstimator { forEGLDTransfer(dataLength: number): number; @@ -10,6 +10,9 @@ interface IGasEstimator { forMultiESDTNFTTransfer(dataLength: number, numTransfers: number): number; } +/** + * @deprecated Use {@link TransfersFactory} instead (same interface, different name). + */ export class TransactionFactory { private readonly gasEstimator; diff --git a/src/transactionPayload.ts b/src/transactionPayload.ts index e92588e0..23393cb4 100644 --- a/src/transactionPayload.ts +++ b/src/transactionPayload.ts @@ -1,7 +1,7 @@ import { ContractCallPayloadBuilder, ContractDeployPayloadBuilder, ContractUpgradePayloadBuilder } from "./smartcontracts/transactionPayloadBuilders"; /** - * The "data" field of a {@link Transaction}, as an immutable object. + * The "data" field of a Transaction, as an immutable object. */ export class TransactionPayload { private readonly data: Buffer; @@ -66,22 +66,22 @@ export class TransactionPayload { } /** - * Returns a new builder, to be used for contract deploy transactions. - */ + * @deprecated Use {@link SmartContract} to create smart contracts related transactions. + */ static contractDeploy(): ContractDeployPayloadBuilder { return new ContractDeployPayloadBuilder(); } /** - * Returns a new builder, to be used for contract upgrade transactions. - */ + * @deprecated Use {@link SmartContract} to create smart contracts related transactions. + */ static contractUpgrade(): ContractUpgradePayloadBuilder { return new ContractUpgradePayloadBuilder(); } /** - * Returns a new builder, to be used for contract call transactions. - */ + * @deprecated Use {@link SmartContract} to create smart contracts related transactions. + */ static contractCall(): ContractCallPayloadBuilder { return new ContractCallPayloadBuilder(); } diff --git a/src/transfersFactory.ts b/src/transfersFactory.ts new file mode 100644 index 00000000..063794b4 --- /dev/null +++ b/src/transfersFactory.ts @@ -0,0 +1,4 @@ +import { TransactionFactory } from "./transactionFactory"; + +export class TransfersFactory extends TransactionFactory { +} diff --git a/tsconfig.docs.json b/tsconfig.docs.json deleted file mode 100644 index cfc830a6..00000000 --- a/tsconfig.docs.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "compilerOptions": { - "module": "CommonJS", - "target": "es2015", - "outDir": "out", - "lib": [ - "ES2015", - "DOM" - ], - "sourceMap": true, - "allowJs": true, - "strict": true, - "strictPropertyInitialization": true, - "strictNullChecks": true, - "skipLibCheck": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUnusedParameters": true, - "esModuleInterop": true, - "declaration": true - }, - "include": [ - "src/**/*" - ], - "exclude": [ - ], - "typedocOptions": { - "entryPoints": [ - "./src/_docs/basic.ts", - "./src/_docs/smartcontracts.ts", - "./src/_docs/errors.ts", - "./src/_docs/typesystem.ts", - "./src/_docs/codec.ts" - ], - "excludePrivate": true, - "includeVersion": true - } -} diff --git a/tsconfig.json b/tsconfig.json index ed256fe9..716261e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,8 +29,6 @@ "out-browser", "out-browser-tests", "**/*.spec.ts", - "src/testutils", - "src/examples", - "src/_docs" + "src/testutils" ] }