diff --git a/package-lock.json b/package-lock.json index b74e4578..a6302b49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "13.1.0", + "version": "13.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "13.1.0", + "version": "13.2.1", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -17,8 +17,8 @@ "keccak": "3.0.2" }, "devDependencies": { - "@multiversx/sdk-network-providers": "2.4.1", - "@multiversx/sdk-wallet": "4.4.0", + "@multiversx/sdk-network-providers": "2.4.3", + "@multiversx/sdk-wallet": "4.5.0", "@types/assert": "1.4.6", "@types/chai": "4.2.11", "@types/mocha": "9.1.0", @@ -584,18 +584,29 @@ } }, "node_modules/@multiversx/sdk-network-providers": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-network-providers/-/sdk-network-providers-2.4.1.tgz", - "integrity": "sha512-AyKLxt51v4Y94NC3/0witz7XPpZ6+2mOi8CVW+j7HP6RtDl5vJinxCriSKb0Z/PzV6LdmJmwCW5iDRebMIk6fg==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-network-providers/-/sdk-network-providers-2.4.3.tgz", + "integrity": "sha512-tJmJuxU+BjtC2q29PuzQOM4Qr6aiXujKwQXgIAPHTiuNbMc3Yi6Q4B0DC1PfI3iG+M4DONwfXknvM1uwqnY2zA==", "dev": true, "dependencies": { - "axios": "1.6.5", + "axios": "1.6.8", "bech32": "1.1.4", "bignumber.js": "9.0.1", "buffer": "6.0.3", "json-bigint": "1.0.0" } }, + "node_modules/@multiversx/sdk-network-providers/node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/@multiversx/sdk-transaction-decoder": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@multiversx/sdk-transaction-decoder/-/sdk-transaction-decoder-1.0.2.tgz", @@ -610,9 +621,9 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "node_modules/@multiversx/sdk-wallet": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.4.0.tgz", - "integrity": "sha512-wS4P8a2ts3cNaSLUw9VFf4yhWSMTYng+nyHKi3/9QalLP5lxBumUfD/mUkb9sK13UPJ5Xp/zB3j8a4Qdllw2Ag==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.5.0.tgz", + "integrity": "sha512-2E/bMDcbFV7zl0KuSEPdNrIjIaMOp4e1BfctPO1q2F0Hd6aAHYO7bZIXNcYEj2v1zgvESybgVh5Zi5vQYtNZRg==", "dev": true, "dependencies": { "@multiversx/sdk-bls-wasm": "0.3.5", @@ -2858,15 +2869,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2987,15 +2989,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -3008,15 +3001,6 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -6588,16 +6572,29 @@ "dev": true }, "@multiversx/sdk-network-providers": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-network-providers/-/sdk-network-providers-2.4.1.tgz", - "integrity": "sha512-AyKLxt51v4Y94NC3/0witz7XPpZ6+2mOi8CVW+j7HP6RtDl5vJinxCriSKb0Z/PzV6LdmJmwCW5iDRebMIk6fg==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-network-providers/-/sdk-network-providers-2.4.3.tgz", + "integrity": "sha512-tJmJuxU+BjtC2q29PuzQOM4Qr6aiXujKwQXgIAPHTiuNbMc3Yi6Q4B0DC1PfI3iG+M4DONwfXknvM1uwqnY2zA==", "dev": true, "requires": { - "axios": "1.6.5", + "axios": "1.6.8", "bech32": "1.1.4", "bignumber.js": "9.0.1", "buffer": "6.0.3", "json-bigint": "1.0.0" + }, + "dependencies": { + "axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dev": true, + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + } } }, "@multiversx/sdk-transaction-decoder": { @@ -6616,9 +6613,9 @@ } }, "@multiversx/sdk-wallet": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.4.0.tgz", - "integrity": "sha512-wS4P8a2ts3cNaSLUw9VFf4yhWSMTYng+nyHKi3/9QalLP5lxBumUfD/mUkb9sK13UPJ5Xp/zB3j8a4Qdllw2Ag==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.5.0.tgz", + "integrity": "sha512-2E/bMDcbFV7zl0KuSEPdNrIjIaMOp4e1BfctPO1q2F0Hd6aAHYO7bZIXNcYEj2v1zgvESybgVh5Zi5vQYtNZRg==", "dev": true, "requires": { "@multiversx/sdk-bls-wasm": "0.3.5", @@ -8353,12 +8350,6 @@ "estraverse": "^5.2.0" } }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -8492,14 +8483,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -8509,14 +8492,6 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { diff --git a/package.json b/package.json index df883f36..9a8f16d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "13.1.0", + "version": "13.2.1", "description": "MultiversX SDK for JavaScript and TypeScript", "main": "out/index.js", "types": "out/index.d.js", @@ -34,8 +34,8 @@ "keccak": "3.0.2" }, "devDependencies": { - "@multiversx/sdk-network-providers": "2.4.1", - "@multiversx/sdk-wallet": "4.4.0", + "@multiversx/sdk-network-providers": "2.4.3", + "@multiversx/sdk-wallet": "4.5.0", "@types/assert": "1.4.6", "@types/chai": "4.2.11", "@types/mocha": "9.1.0", diff --git a/src/address.spec.ts b/src/address.spec.ts index 7acbd999..46552556 100644 --- a/src/address.spec.ts +++ b/src/address.spec.ts @@ -19,6 +19,18 @@ describe("test address", () => { assert.equal(new Address(new Uint8Array(Buffer.from(bobHex, "hex"))).toHex(), bobHex); }); + it("should create address (custom hrp)", async () => { + let address = Address.fromHex(aliceHex, "test"); + assert.deepEqual(address.getPublicKey(), Buffer.from(aliceHex, "hex")); + assert.equal(address.getHrp(), "test"); + assert.equal(address.toBech32(), "test1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ss5hqhtr"); + + address = Address.fromHex(bobHex, "xerd"); + assert.deepEqual(address.getPublicKey(), Buffer.from(bobHex, "hex")); + assert.equal(address.getHrp(), "xerd"); + assert.equal(address.toBech32(), "xerd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruq9thc9j"); + }); + it("should create empty address", async () => { const nobody = Address.empty(); diff --git a/src/address.ts b/src/address.ts index e694c001..00241fee 100644 --- a/src/address.ts +++ b/src/address.ts @@ -1,15 +1,11 @@ import * as bech32 from "bech32"; import BigNumber from "bignumber.js"; +import { LibraryConfig } from "./config"; import { CURRENT_NUMBER_OF_SHARDS_WITHOUT_META, METACHAIN_ID, WasmVirtualMachine } from "./constants"; import * as errors from "./errors"; import { bigIntToBuffer } from "./tokenOperations/codec"; const createKeccakHash = require("keccak"); -/** - * The human-readable-part of the bech32 addresses. - */ -const HRP = "erd"; - /** * The length (in bytes) of a public key (from which a bech32 address can be obtained). */ @@ -26,77 +22,131 @@ interface IAddress { * An Address, as an immutable object. */ export class Address { - // We keep a hex-encoded string as the "backing" value - private valueHex: string = ""; + private readonly publicKey: Buffer; + private readonly hrp: string; /** * Creates an address object, given a raw string (whether a hex pubkey or a Bech32 address), a sequence of bytes, or another Address object. */ - public constructor(value: Address | Buffer | Uint8Array | string) { + public constructor(value: Address | Uint8Array | string, hrp?: string) { + // Legacy flow. if (!value) { + this.publicKey = Buffer.from([]); + this.hrp = hrp || LibraryConfig.DefaultAddressHrp; + return; } - if (value instanceof Address) { - return Address.fromAddress(value); - } + + // The only flow that's following the specs. if (ArrayBuffer.isView(value)) { - return Address.fromBuffer(Buffer.from(value)); + if (value.length != PUBKEY_LENGTH) { + throw new errors.ErrAddressCannotCreate(value); + } + + this.publicKey = Buffer.from(value); + this.hrp = hrp || LibraryConfig.DefaultAddressHrp; + + return; } + + // Legacy flow. + if (value instanceof Address) { + if (hrp) { + throw new errors.ErrInvalidArgument( + "this variant of the Address constructor does not accept the 'hrp' argument", + ); + } + + this.publicKey = value.publicKey; + this.hrp = value.hrp; + + return; + } + + // Legacy flow. if (typeof value === "string") { - return Address.fromString(value); + if (Address.isValidHex(value)) { + this.publicKey = Buffer.from(value, "hex"); + this.hrp = hrp || LibraryConfig.DefaultAddressHrp; + + return; + } + + if (hrp) { + throw new errors.ErrInvalidArgument( + "this variant of the Address constructor does not accept the 'hrp' argument", + ); + } + + // On this legacy flow, we do not accept addresses with custom hrp (in order to avoid behavioral breaking changes). + const { hrp: decodedHrp, pubkey } = decodeFromBech32({ value, allowCustomHrp: false }); + this.publicKey = pubkey; + this.hrp = decodedHrp; + + return; } throw new errors.ErrAddressCannotCreate(value); } /** - * Creates an address object from another address object + * Creates an address object from a bech32-encoded string */ - static fromAddress(address: Address): Address { - return Address.fromValidHex(address.valueHex); + static newFromBech32(value: string): Address { + const { hrp, pubkey } = decodeFromBech32({ value, allowCustomHrp: true }); + return new Address(pubkey, hrp); } - private static fromValidHex(value: string): Address { - let result = Address.empty(); - result.valueHex = value; - return result; + /** + * Use {@link newFromBech32} instead. + */ + static fromBech32(value: string): Address { + // On this legacy flow, we do not accept addresses with custom hrp (in order to avoid behavioral breaking changes). + const { hrp, pubkey } = decodeFromBech32({ value, allowCustomHrp: false }); + return new Address(pubkey, hrp); } /** - * Creates an address object from a Buffer + * Creates an address object from a hex-encoded string */ - static fromBuffer(buffer: Buffer): Address { - if (buffer.length != PUBKEY_LENGTH) { - throw new errors.ErrAddressCannotCreate(buffer); + static newFromHex(value: string, hrp?: string): Address { + if (!Address.isValidHex(value)) { + throw new errors.ErrAddressCannotCreate(value); } - return Address.fromValidHex(buffer.toString("hex")); + return new Address(Buffer.from(value, "hex"), hrp); } /** - * Creates an address object from a string (hex or bech32) + * Use {@link newFromHex} instead. */ - static fromString(value: string): Address { - if (Address.isValidHex(value)) { - return Address.fromValidHex(value); - } + static fromHex(value: string, hrp?: string): Address { + return Address.newFromHex(value, hrp); + } - return Address.fromBech32(value); + /** + * @deprecated Constructing an address object from another object is deprecated. + */ + static fromAddress(address: Address): Address { + return new Address(address); } - private static isValidHex(value: string) { - return Buffer.from(value, "hex").length == PUBKEY_LENGTH; + /** + * @deprecated Use the constructor, instead. + */ + static fromBuffer(buffer: Buffer, hrp?: string): Address { + return new Address(buffer, hrp); } /** - * Creates an address object from a hex-encoded string + * @deprecated Use {@link newFromBech32} or {@link newFromHex}. */ - static fromHex(value: string): Address { - if (!Address.isValidHex(value)) { - throw new errors.ErrAddressCannotCreate(value); - } + static fromString(value: string, hrp?: string): Address { + return new Address(value, hrp); + } - return Address.fromValidHex(value); + private static isValidHex(value: string) { + return Buffer.from(value, "hex").length == PUBKEY_LENGTH; } /** @@ -107,31 +157,6 @@ export class Address { return new Address(""); } - /** - * Creates an address object from a bech32-encoded string - */ - static fromBech32(value: string): Address { - let decoded; - - try { - decoded = bech32.decode(value); - } catch (err: any) { - throw new errors.ErrAddressCannotCreate(value, err); - } - - const prefix = decoded.prefix; - if (prefix != HRP) { - throw new errors.ErrAddressBadHrp(HRP, prefix); - } - - const pubkey = Buffer.from(bech32.fromWords(decoded.words)); - if (pubkey.length != PUBKEY_LENGTH) { - throw new errors.ErrAddressCannotCreate(value); - } - - return Address.fromValidHex(pubkey.toString("hex")); - } - /** * Performs address validation without throwing errors */ @@ -140,7 +165,7 @@ export class Address { const prefix = decoded?.prefix; const pubkey = decoded ? Buffer.from(bech32.fromWords(decoded.words)) : undefined; - if (prefix !== HRP || pubkey?.length !== PUBKEY_LENGTH) { + if (prefix !== LibraryConfig.DefaultAddressHrp || pubkey?.length !== PUBKEY_LENGTH) { return false; } @@ -162,7 +187,7 @@ export class Address { return ""; } - return this.valueHex; + return this.publicKey.toString("hex"); } /** @@ -181,7 +206,7 @@ export class Address { } let words = bech32.toWords(this.pubkey()); - let address = bech32.encode(HRP, words); + let address = bech32.encode(this.hrp, words); return address; } @@ -196,26 +221,21 @@ export class Address { * Returns the pubkey as raw bytes (buffer) */ getPublicKey(): Buffer { - if (this.isEmpty()) { - return Buffer.from([]); - } - - return Buffer.from(this.valueHex, "hex"); + return this.publicKey; } /** * Returns the human-readable-part of the bech32 addresses. - * The HRP is currently hardcoded to "erd". */ getHrp(): string { - return HRP; + return this.hrp; } /** * Returns whether the address is empty. */ isEmpty() { - return !this.valueHex; + return this.publicKey.length == 0; } /** @@ -226,7 +246,7 @@ export class Address { return false; } - return this.valueHex == other.valueHex; + return this.publicKey.toString() == other.publicKey.toString(); } /** @@ -334,3 +354,28 @@ export class AddressComputer { return false; } } + +function decodeFromBech32(options: { value: string; allowCustomHrp: boolean }): { hrp: string; pubkey: Buffer } { + const value = options.value; + const allowCustomHrp = options.allowCustomHrp; + + let hrp: string; + let pubkey: Buffer; + + try { + const decoded = bech32.decode(value); + + hrp = decoded.prefix; + pubkey = Buffer.from(bech32.fromWords(decoded.words)); + } catch (err: any) { + throw new errors.ErrAddressCannotCreate(value, err); + } + + // Workaround, in order to avoid behavioral breaking changes on legacy flows. + // In a future major release, we should drop this constraint (not exactly useful, validation should be performed in other ways) + if (!allowCustomHrp && hrp != LibraryConfig.DefaultAddressHrp) { + throw new errors.ErrAddressBadHrp(LibraryConfig.DefaultAddressHrp, hrp); + } + + return { hrp, pubkey }; +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000..cf60ce5a --- /dev/null +++ b/src/config.ts @@ -0,0 +1,15 @@ +/** + * Global configuration of the library. + * + * Generally speaking, this configuration should only be altered on exotic use cases; + * it can be seen as a collection of constants (or, to be more precise, rarely changed variables) that are used throughout the library. + * + * Never alter the configuration within a library! + * Only alter the configuration (if needed) within an (end) application that uses this library. + */ +export class LibraryConfig { + /** + * The human-readable-part of the bech32 addresses. + */ + public static DefaultAddressHrp: string = "erd"; +} diff --git a/src/constants.ts b/src/constants.ts index 2a249392..e19c22ef 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,16 +11,41 @@ export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer"; export const ESDT_TRANSFER_VALUE = "0"; export const ARGUMENTS_SEPARATOR = "@"; export const VM_TYPE_WASM_VM = new Uint8Array([0x05, 0x00]); -export const CONTRACT_DEPLOY_ADDRESS = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"; -export const DELEGATION_MANAGER_SC_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6"; -export const DEFAULT_HRP = "erd"; -export const ESDT_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; +export const CONTRACT_DEPLOY_ADDRESS_HEX = "0000000000000000000000000000000000000000000000000000000000000000"; +export const DELEGATION_MANAGER_SC_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000004ffff"; +export const ESDT_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000002ffff"; + export const DEFAULT_MESSAGE_VERSION = 1; export const MESSAGE_PREFIX = "\x17Elrond Signed Message:\n"; export const HEX_TRANSACTION_HASH_LENGTH = 64; -export const BECH32_ADDRESS_LENGTH = 62; + export const CURRENT_NUMBER_OF_SHARDS_WITHOUT_META = 3; export const WasmVirtualMachine = "0500"; export const METACHAIN_ID = 4294967295; export const SDK_JS_SIGNER = "sdk-js"; export const UNKNOWN_SIGNER = "unknown"; + +/** + * @deprecated + */ +export const DEFAULT_HRP = "erd"; + +/** + * @deprecated + */ +export const BECH32_ADDRESS_LENGTH = 62; + +/** + * @deprecated Use {@link CONTRACT_DEPLOY_ADDRESS_HEX} instead. + */ +export const CONTRACT_DEPLOY_ADDRESS = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"; + +/** + * @deprecated Use {@link DELEGATION_MANAGER_SC_ADDRESS_HEX} instead. + */ +export const DELEGATION_MANAGER_SC_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6"; + +/** + * @deprecated Use {@link 000000000000000000010000000000000000000000000000000000000002ffff} instead. + */ +export const ESDT_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; diff --git a/src/errors.ts b/src/errors.ts index 30310edc..2bb4ff22 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -372,3 +372,15 @@ export class ErrParseTransactionOutcome extends Err { super(message); } } + +/** + * Signals an error when querying a smart contract. + */ +export class ErrSmartContractQuery extends Err { + public returnCode: string; + + public constructor(returnCode: string, message: string) { + super(message); + this.returnCode = returnCode; + } +} diff --git a/src/index.ts b/src/index.ts index bfc9a49c..d244d0e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ export * from "./account"; export * from "./adapters"; export * from "./address"; export * from "./asyncTimer"; +export * from "./config"; export * from "./converters"; export * from "./errors"; export * from "./gasEstimator"; diff --git a/src/interfaceOfNetwork.ts b/src/interfaceOfNetwork.ts index 4fefd39e..52674423 100644 --- a/src/interfaceOfNetwork.ts +++ b/src/interfaceOfNetwork.ts @@ -33,6 +33,8 @@ export interface ITransactionStatus { isFailed(): boolean; isInvalid(): boolean; isExecuted(): boolean; + isSuccessful(): boolean; + valueOf(): string; } export interface ITransactionReceipt { diff --git a/src/proto/serializer.spec.ts b/src/proto/serializer.spec.ts index f92acf0e..97a88b5c 100644 --- a/src/proto/serializer.spec.ts +++ b/src/proto/serializer.spec.ts @@ -23,14 +23,17 @@ describe("serialize transactions", () => { sender: wallets.alice.address, receiver: wallets.bob.address, gasLimit: 50000, - chainID: "local-testnet" + chainID: "local-testnet", }); const signer = wallets.alice.signer; transaction.applySignature(await signer.sign(transaction.serializeForSigning())); let buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "0859120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340d08603520d6c6f63616c2d746573746e6574580262403f08a1dd64fbb627d10b048e0b45b1390f29bb0e457762a2ccb710b029f299022a67a4b8e45cf62f4314afec2e56b5574c71e38df96cc41fae757b7ee5062503"); + assert.equal( + buffer.toString("hex"), + "0859120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340d08603520d6c6f63616c2d746573746e6574580262403f08a1dd64fbb627d10b048e0b45b1390f29bb0e457762a2ccb710b029f299022a67a4b8e45cf62f4314afec2e56b5574c71e38df96cc41fae757b7ee5062503", + ); }); it("with data, no value", async () => { @@ -41,14 +44,17 @@ describe("serialize transactions", () => { receiver: wallets.bob.address, gasLimit: 80000, data: new TransactionPayload("hello"), - chainID: "local-testnet" + chainID: "local-testnet", }); const signer = wallets.alice.signer; transaction.applySignature(await signer.sign(transaction.serializeForSigning())); let buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "085a120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc034080f1044a0568656c6c6f520d6c6f63616c2d746573746e657458026240f9e8c1caf7f36b99e7e76ee1118bf71b55cde11a2356e2b3adf15f4ad711d2e1982469cbba7eb0afbf74e8a8f78e549b9410cd86eeaa88fcba62611ac9f6e30e"); + assert.equal( + buffer.toString("hex"), + "085a120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc034080f1044a0568656c6c6f520d6c6f63616c2d746573746e657458026240f9e8c1caf7f36b99e7e76ee1118bf71b55cde11a2356e2b3adf15f4ad711d2e1982469cbba7eb0afbf74e8a8f78e549b9410cd86eeaa88fcba62611ac9f6e30e", + ); }); it("with data, with value", async () => { @@ -59,14 +65,17 @@ describe("serialize transactions", () => { receiver: wallets.bob.address, gasLimit: 100000, data: new TransactionPayload("for the book"), - chainID: "local-testnet" + chainID: "local-testnet", }); const signer = wallets.alice.signer; transaction.applySignature(await signer.sign(transaction.serializeForSigning())); let buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "085b1209008ac7230489e800001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340a08d064a0c666f722074686520626f6f6b520d6c6f63616c2d746573746e657458026240b45f22e9f57a6df22670fcc3566723a0711a05ac2547456de59fd222a54940e4a1d99bd414897ccbf5c02a842ad86e638989b7f4d30edd26c99a8cd1eb092304"); + assert.equal( + buffer.toString("hex"), + "085b1209008ac7230489e800001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340a08d064a0c666f722074686520626f6f6b520d6c6f63616c2d746573746e657458026240b45f22e9f57a6df22670fcc3566723a0711a05ac2547456de59fd222a54940e4a1d99bd414897ccbf5c02a842ad86e638989b7f4d30edd26c99a8cd1eb092304", + ); }); it("with data, with large value", async () => { @@ -77,14 +86,17 @@ describe("serialize transactions", () => { receiver: wallets.bob.address, gasLimit: 100000, data: new TransactionPayload("for the spaceship"), - chainID: "local-testnet" + chainID: "local-testnet", }); const signer = wallets.alice.signer; transaction.applySignature(await signer.sign(transaction.serializeForSigning())); let buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "085c120e00018ee90ff6181f3761632000001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340a08d064a11666f722074686520737061636573686970520d6c6f63616c2d746573746e65745802624001f05aa8cb0614e12a94ab9dcbde5e78370a4e05d23ef25a1fb9d5fcf1cb3b1f33b919cd8dafb1704efb18fa233a8aa0d3344fb6ee9b613a7d7a403786ffbd0a"); + assert.equal( + buffer.toString("hex"), + "085c120e00018ee90ff6181f3761632000001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc0340a08d064a11666f722074686520737061636573686970520d6c6f63616c2d746573746e65745802624001f05aa8cb0614e12a94ab9dcbde5e78370a4e05d23ef25a1fb9d5fcf1cb3b1f33b919cd8dafb1704efb18fa233a8aa0d3344fb6ee9b613a7d7a403786ffbd0a", + ); }); it("with nonce = 0", async () => { @@ -96,13 +108,20 @@ describe("serialize transactions", () => { gasLimit: 80000, data: new TransactionPayload("hello"), chainID: "local-testnet", - version: new TransactionVersion(1) + version: new TransactionVersion(1), }); - transaction.applySignature(new Signature("dfa3e9f2fdec60dcb353bac3b3435b4a2ff251e7e98eaf8620f46c731fc70c8ba5615fd4e208b05e75fe0f7dc44b7a99567e29f94fcd91efac7e67b182cd2a04")) + transaction.applySignature( + new Signature( + "dfa3e9f2fdec60dcb353bac3b3435b4a2ff251e7e98eaf8620f46c731fc70c8ba5615fd4e208b05e75fe0f7dc44b7a99567e29f94fcd91efac7e67b182cd2a04", + ), + ); let buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc034080f1044a0568656c6c6f520d6c6f63616c2d746573746e657458016240dfa3e9f2fdec60dcb353bac3b3435b4a2ff251e7e98eaf8620f46c731fc70c8ba5615fd4e208b05e75fe0f7dc44b7a99567e29f94fcd91efac7e67b182cd2a04"); + assert.equal( + buffer.toString("hex"), + "120200001a208049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f82a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1388094ebdc034080f1044a0568656c6c6f520d6c6f63616c2d746573746e657458016240dfa3e9f2fdec60dcb353bac3b3435b4a2ff251e7e98eaf8620f46c731fc70c8ba5615fd4e208b05e75fe0f7dc44b7a99567e29f94fcd91efac7e67b182cd2a04", + ); }); it("with usernames", async () => { @@ -114,13 +133,16 @@ describe("serialize transactions", () => { senderUsername: "carol", receiverUsername: "alice", gasLimit: 50000, - chainID: "T" + chainID: "T", }); const signer = wallets.carol.signer; transaction.applySignature(await signer.sign(transaction.serializeForSigning())); const buffer = serializer.serializeTransaction(transaction); - assert.equal(buffer.toString("hex"), "08cc011209000de0b6b3a76400001a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e12205616c6963652a20b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba32056361726f6c388094ebdc0340d086035201545802624051e6cd78fb3ab4b53ff7ad6864df27cb4a56d70603332869d47a5cf6ea977c30e696103e41e8dddf2582996ad335229fdf4acb726564dbc1a0bc9e705b511f06"); + assert.equal( + buffer.toString("hex"), + "08cc011209000de0b6b3a76400001a200139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e12205616c6963652a20b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba32056361726f6c388094ebdc0340d086035201545802624051e6cd78fb3ab4b53ff7ad6864df27cb4a56d70603332869d47a5cf6ea977c30e696103e41e8dddf2582996ad335229fdf4acb726564dbc1a0bc9e705b511f06", + ); }); }); diff --git a/src/proto/serializer.ts b/src/proto/serializer.ts index 9e1565b0..8852dcd7 100644 --- a/src/proto/serializer.ts +++ b/src/proto/serializer.ts @@ -25,15 +25,19 @@ export class ProtoSerializer { Nonce: transaction.getNonce().valueOf() ? transaction.getNonce().valueOf() : undefined, Value: this.serializeTransactionValue(transaction.getValue()), RcvAddr: receiverPubkey, - RcvUserName: transaction.getReceiverUsername() ? Buffer.from(transaction.getReceiverUsername()).toString("base64") : undefined, + RcvUserName: transaction.getReceiverUsername() + ? Buffer.from(transaction.getReceiverUsername()).toString("base64") + : undefined, SndAddr: senderPubkey, - SndUserName: transaction.getSenderUsername() ? Buffer.from(transaction.getSenderUsername()).toString("base64") : undefined, + SndUserName: transaction.getSenderUsername() + ? Buffer.from(transaction.getSenderUsername()).toString("base64") + : undefined, GasPrice: transaction.getGasPrice().valueOf(), GasLimit: transaction.getGasLimit().valueOf(), Data: transaction.getData().length() == 0 ? null : transaction.getData().valueOf(), ChainID: Buffer.from(transaction.getChainID().valueOf()), Version: transaction.getVersion().valueOf(), - Signature: transaction.getSignature() + Signature: transaction.getSignature(), }); if (transaction.getOptions().valueOf() !== TRANSACTION_OPTIONS_DEFAULT) { diff --git a/src/relayedTransactionV1Builder.spec.ts b/src/relayedTransactionV1Builder.spec.ts index 55bbc9c0..018ba6b0 100644 --- a/src/relayedTransactionV1Builder.spec.ts +++ b/src/relayedTransactionV1Builder.spec.ts @@ -34,7 +34,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; builder.setNetworkConfig(networkConfig); assert.throw(() => builder.build(), errors.ErrInvalidRelayedV1BuilderArguments); @@ -48,7 +48,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -73,8 +73,14 @@ describe("test relayed v1 transaction builder", function () { relayedTxV1.applySignature(await alice.signer.sign(relayedTxV1.serializeForSigning())); assert.equal(relayedTxV1.getNonce().valueOf(), 2627); - assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2272525455544858677a4273496e4f6e454b6b7869642b354e66524d486e33534948314673746f577352434c434b3258514c41614f4e704449346531476173624c5150616130566f364144516d4f2b52446b6f364a43413d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a327d"); - assert.equal(relayedTxV1.getSignature().toString("hex"), "128e7cdc14c2b9beee2f3ff7a7fa5d1f5ef31a654a0c92e223c90ab28265fa277d306f23a06536248cf9573e828017004fb639617fade4d68a37524aafca710d"); + assert.equal( + relayedTxV1.getData().toString(), + "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2272525455544858677a4273496e4f6e454b6b7869642b354e66524d486e33534948314673746f577352434c434b3258514c41614f4e704449346531476173624c5150616130566f364144516d4f2b52446b6f364a43413d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a327d", + ); + assert.equal( + relayedTxV1.getSignature().toString("hex"), + "128e7cdc14c2b9beee2f3ff7a7fa5d1f5ef31a654a0c92e223c90ab28265fa277d306f23a06536248cf9573e828017004fb639617fade4d68a37524aafca710d", + ); }); it("should compute relayed v1 transaction (with usernames)", async function () { @@ -82,7 +88,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -93,7 +99,7 @@ describe("test relayed v1 transaction builder", function () { senderUsername: "carol", receiverUsername: "alice", gasLimit: 50000, - chainID: networkConfig.ChainID + chainID: networkConfig.ChainID, }); innerTx.applySignature(await carol.signer.sign(innerTx.serializeForSigning())); @@ -109,8 +115,14 @@ describe("test relayed v1 transaction builder", function () { relayedTxV1.applySignature(await frank.signer.sign(relayedTxV1.serializeForSigning())); assert.equal(relayedTxV1.getNonce().valueOf(), 715); - assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3230382c2273656e646572223a227371455656633553486b6c45344a717864556e59573068397a536249533141586f3534786f32634969626f3d222c227265636569766572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c2276616c7565223a313030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a226a33427a6469554144325963517473576c65707663664a6f75657a48573063316b735a424a4d6339573167435450512b6870636759457858326f6f367a4b5654347464314b4b6f79783841526a346e336474576c44413d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c22736e64557365724e616d65223a22593246796232773d222c22726376557365724e616d65223a22595778705932553d227d"); - assert.equal(relayedTxV1.getSignature().toString("hex"), "3787d640e5a579e7977a4a1bcdd435ad11855632fa4a414a06fbf8355692d1a58d76ef0adbdd6ccd6bd3c329f36bd53c180d4873ec1a6c558e659aeb9ab92d00"); + assert.equal( + relayedTxV1.getData().toString(), + "relayedTx@7b226e6f6e6365223a3230382c2273656e646572223a227371455656633553486b6c45344a717864556e59573068397a536249533141586f3534786f32634969626f3d222c227265636569766572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c2276616c7565223a313030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a226a33427a6469554144325963517473576c65707663664a6f75657a48573063316b735a424a4d6339573167435450512b6870636759457858326f6f367a4b5654347464314b4b6f79783841526a346e336474576c44413d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c22736e64557365724e616d65223a22593246796232773d222c22726376557365724e616d65223a22595778705932553d227d", + ); + assert.equal( + relayedTxV1.getSignature().toString("hex"), + "3787d640e5a579e7977a4a1bcdd435ad11855632fa4a414a06fbf8355692d1a58d76ef0adbdd6ccd6bd3c329f36bd53c180d4873ec1a6c558e659aeb9ab92d00", + ); }); it("should compute relayed v1 transaction with big value", async function () { @@ -118,7 +130,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -129,7 +141,7 @@ describe("test relayed v1 transaction builder", function () { senderUsername: "carol", receiverUsername: "alice", gasLimit: 50000, - chainID: networkConfig.ChainID + chainID: networkConfig.ChainID, }); innerTx.applySignature(await carol.signer.sign(innerTx.serializeForSigning())); @@ -145,8 +157,14 @@ describe("test relayed v1 transaction builder", function () { relayedTxV1.applySignature(await frank.signer.sign(relayedTxV1.serializeForSigning())); assert.equal(relayedTxV1.getNonce().valueOf(), 715); - assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3230382c2273656e646572223a227371455656633553486b6c45344a717864556e59573068397a536249533141586f3534786f32634969626f3d222c227265636569766572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c2276616c7565223a313939393939393030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a22594661677972512f726d614c7333766e7159307657553858415a7939354b4e31725738347a4f764b62376c7a3773576e2f566a546d68704378774d682b7261314e444832574d6f3965507648304f79427453776a44773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c22736e64557365724e616d65223a22593246796232773d222c22726376557365724e616d65223a22595778705932553d227d"); - assert.equal(relayedTxV1.getSignature().toString("hex"), "c0fb5cf8c0a413d6988ba35dc279c63f8849572c5f23b1cab36dcc50952dc3ed9da01068d6ac0cbde7e14167bfc2eca5164d5c2154c89eb313c9c596e3f8b801"); + assert.equal( + relayedTxV1.getData().toString(), + "relayedTx@7b226e6f6e6365223a3230382c2273656e646572223a227371455656633553486b6c45344a717864556e59573068397a536249533141586f3534786f32634969626f3d222c227265636569766572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c2276616c7565223a313939393939393030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a22594661677972512f726d614c7333766e7159307657553858415a7939354b4e31725738347a4f764b62376c7a3773576e2f566a546d68704378774d682b7261314e444832574d6f3965507648304f79427453776a44773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c22736e64557365724e616d65223a22593246796232773d222c22726376557365724e616d65223a22595778705932553d227d", + ); + assert.equal( + relayedTxV1.getSignature().toString("hex"), + "c0fb5cf8c0a413d6988ba35dc279c63f8849572c5f23b1cab36dcc50952dc3ed9da01068d6ac0cbde7e14167bfc2eca5164d5c2154c89eb313c9c596e3f8b801", + ); }); it("should compute guarded inner Tx - relayed v1 transaction", async function () { @@ -154,7 +172,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -166,7 +184,7 @@ describe("test relayed v1 transaction builder", function () { data: new TransactionPayload("getContractConfig"), guardian: grace.address, version: TransactionVersion.withTxOptions(), - options: TransactionOptions.withOptions({ guarded: true }) + options: TransactionOptions.withOptions({ guarded: true }), }); innerTx.applySignature(await bob.signer.sign(innerTx.serializeForSigning())); @@ -183,8 +201,14 @@ describe("test relayed v1 transaction builder", function () { relayedTxV1.applySignature(await alice.signer.sign(relayedTxV1.serializeForSigning())); assert.equal(relayedTxV1.getNonce().valueOf(), 2627); - assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414146414b565841323879704877692f79693741364c64504b704f68464d386958513d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a224b4b78324f33383655725135416b4f465258307578327933446a384853334b373038487174344668377161557669424550716c45614e746e6158706a6f2f333651476d4a456934784435457a6c6f4f677a634d4442773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c226f7074696f6e73223a322c22677561726469616e223a22486f714c61306e655733766843716f56696c70715372744c5673774939535337586d7a563868477450684d3d222c22677561726469616e5369676e6174757265223a222b5431526f4833625a792f54423177342b6a365155477258645637457577553073753948646551626453515269463953757a686d634b705463526d58595252366c534c6652394931624d7134674730436538363741513d3d227d"); - assert.equal(relayedTxV1.getSignature().toString("hex"), "39cff9d5100e290fbc7361cb6e2402261caf864257b4116f150e0c61e7869155dff8361fa5449431eb7a8ed847c01ba9b3b5ebafe5fac1a3d40c64829d827e00"); + assert.equal( + relayedTxV1.getData().toString(), + "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414146414b565841323879704877692f79693741364c64504b704f68464d386958513d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a224b4b78324f33383655725135416b4f465258307578327933446a384853334b373038487174344668377161557669424550716c45614e746e6158706a6f2f333651476d4a456934784435457a6c6f4f677a634d4442773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c226f7074696f6e73223a322c22677561726469616e223a22486f714c61306e655733766843716f56696c70715372744c5673774939535337586d7a563868477450684d3d222c22677561726469616e5369676e6174757265223a222b5431526f4833625a792f54423177342b6a365155477258645637457577553073753948646551626453515269463953757a686d634b705463526d58595252366c534c6652394931624d7134674730436538363741513d3d227d", + ); + assert.equal( + relayedTxV1.getSignature().toString("hex"), + "39cff9d5100e290fbc7361cb6e2402261caf864257b4116f150e0c61e7869155dff8361fa5449431eb7a8ed847c01ba9b3b5ebafe5fac1a3d40c64829d827e00", + ); }); it("should compute guarded inner tx and guarded relayed v1 transaction", async function () { @@ -192,7 +216,7 @@ describe("test relayed v1 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -204,7 +228,7 @@ describe("test relayed v1 transaction builder", function () { data: new TransactionPayload("addNumber"), guardian: grace.address, version: TransactionVersion.withTxOptions(), - options: TransactionOptions.withOptions({ guarded: true }) + options: TransactionOptions.withOptions({ guarded: true }), }); innerTx.applySignature(await bob.signer.sign(innerTx.serializeForSigning())); @@ -224,7 +248,13 @@ describe("test relayed v1 transaction builder", function () { relayedTxV1.applyGuardianSignature(await frank.signer.sign(relayedTxV1.serializeForSigning())); assert.equal(relayedTxV1.getNonce().valueOf(), 2627); - assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414146414b565841323879704877692f79693741364c64504b704f68464d386958513d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225957526b546e5674596d5679222c227369676e6174757265223a223469724d4b4a656d724d375174344e7635487633544c44683775654779487045564c4371674a3677652f7a662b746a4933354975573452633458543451533433475333356158386c6a533834324a38426854645043673d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c226f7074696f6e73223a322c22677561726469616e223a22486f714c61306e655733766843716f56696c70715372744c5673774939535337586d7a563868477450684d3d222c22677561726469616e5369676e6174757265223a2270424754394e674a78307539624c56796b654d78786a454865374269696c37764932324a46676f32787a6e2f496e3032463769546563356b44395045324f747065386c475335412b532f4a36417762576834446744673d3d227d"); - assert.equal(relayedTxV1.getSignature().toString("hex"), "8ede1bbeed96b102344dffeac12c2592c62b7313cdeb132e8c8bf11d2b1d3bb8189d257a6dbcc99e222393d9b9ec77656c349dae97a32e68bdebd636066bf706"); + assert.equal( + relayedTxV1.getData().toString(), + "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414146414b565841323879704877692f79693741364c64504b704f68464d386958513d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225957526b546e5674596d5679222c227369676e6174757265223a223469724d4b4a656d724d375174344e7635487633544c44683775654779487045564c4371674a3677652f7a662b746a4933354975573452633458543451533433475333356158386c6a533834324a38426854645043673d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a322c226f7074696f6e73223a322c22677561726469616e223a22486f714c61306e655733766843716f56696c70715372744c5673774939535337586d7a563868477450684d3d222c22677561726469616e5369676e6174757265223a2270424754394e674a78307539624c56796b654d78786a454865374269696c37764932324a46676f32787a6e2f496e3032463769546563356b44395045324f747065386c475335412b532f4a36417762576834446744673d3d227d", + ); + assert.equal( + relayedTxV1.getSignature().toString("hex"), + "8ede1bbeed96b102344dffeac12c2592c62b7313cdeb132e8c8bf11d2b1d3bb8189d257a6dbcc99e222393d9b9ec77656c349dae97a32e68bdebd636066bf706", + ); }); }); diff --git a/src/relayedTransactionV2Builder.spec.ts b/src/relayedTransactionV2Builder.spec.ts index b72823c2..cb4cd530 100644 --- a/src/relayedTransactionV2Builder.spec.ts +++ b/src/relayedTransactionV2Builder.spec.ts @@ -25,7 +25,7 @@ describe("test relayed v2 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -46,7 +46,7 @@ describe("test relayed v2 transaction builder", function () { innerTx.setGasLimit({ valueOf: function () { return 10; - } + }, }); builder = builder.setNetworkConfig(networkConfig).setInnerTransactionGasLimit(10).setInnerTransaction(innerTx); assert.throw(() => builder.build(), errors.ErrGasLimitShouldBe0ForInnerTransaction); @@ -57,7 +57,7 @@ describe("test relayed v2 transaction builder", function () { MinGasLimit: 50_000, GasPerDataByte: 1_500, GasPriceModifier: 0.01, - ChainID: "T" + ChainID: "T", }; const innerTx = new Transaction({ @@ -88,8 +88,7 @@ describe("test relayed v2 transaction builder", function () { assert.equal(relayedTxV2.getVersion().valueOf(), 2); assert.equal( relayedTxV2.getData().toString(), - "relayedTxV2@000000000000000000010000000000000000000000000000000000000002ffff@0f@676574436f6e7472616374436f6e666967@fc3ed87a51ee659f937c1a1ed11c1ae677e99629fae9cc289461f033e6514d1a8cfad1144ae9c1b70f28554d196bd6ba1604240c1c1dc19c959e96c1c3b62d0c"); + "relayedTxV2@000000000000000000010000000000000000000000000000000000000002ffff@0f@676574436f6e7472616374436f6e666967@fc3ed87a51ee659f937c1a1ed11c1ae677e99629fae9cc289461f033e6514d1a8cfad1144ae9c1b70f28554d196bd6ba1604240c1c1dc19c959e96c1c3b62d0c", + ); }); }); - - diff --git a/src/signableMessage.spec.ts b/src/signableMessage.spec.ts index e8843f8f..4480b66d 100644 --- a/src/signableMessage.spec.ts +++ b/src/signableMessage.spec.ts @@ -2,7 +2,6 @@ import { assert } from "chai"; import { SignableMessage } from "./signableMessage"; import { loadTestWallets, TestWallet } from "./testutils"; - describe("test signable message", () => { let alice: TestWallet; before(async function () { @@ -13,18 +12,23 @@ describe("test signable message", () => { address: alice.address, message: Buffer.from("test message", "ascii"), signature: Buffer.from("a".repeat(128), "hex"), - signer: "ElrondWallet" + signer: "ElrondWallet", }); const jsonSM = sm.toJSON(); // We just test that the returned object contains what was passed and the hex values are prefixed with 0x - assert.deepEqual(jsonSM, { - address: 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', - message: '0x74657374206d657373616765', - signature: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - version: 1, - signer: 'ElrondWallet' - }, "invalid signable message returned"); + assert.deepEqual( + jsonSM, + { + address: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + message: "0x74657374206d657373616765", + signature: + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + version: 1, + signer: "ElrondWallet", + }, + "invalid signable message returned", + ); }); }); diff --git a/src/smartContractQueriesController.ts b/src/smartContractQueriesController.ts index 233d8337..d156742f 100644 --- a/src/smartContractQueriesController.ts +++ b/src/smartContractQueriesController.ts @@ -1,7 +1,8 @@ -import { Err } from "./errors"; +import { Err, ErrSmartContractQuery } from "./errors"; import { IContractQueryResponse } from "./interfaceOfNetwork"; import { SmartContractQuery, SmartContractQueryResponse } from "./smartContractQuery"; import { ArgSerializer, ContractFunction, EndpointDefinition, NativeSerializer, ResultsParser } from "./smartcontracts"; +import { isTyped } from "./smartcontracts/typesystem"; interface IAbi { getEndpoint(name: string | ContractFunction): EndpointDefinition; @@ -22,6 +23,26 @@ export class SmartContractQueriesController { this.legacyResultsParser = new ResultsParser(); } + async query(options: { + contract: string; + caller?: string; + value?: bigint; + function: string; + arguments: any[]; + }): Promise { + const query = this.createQuery(options); + const queryResponse = await this.runQuery(query); + this.raiseForStatus(queryResponse); + return this.parseQueryResponse(queryResponse); + } + + private raiseForStatus(queryResponse: SmartContractQueryResponse): void { + const isOk = queryResponse.returnCode === "ok"; + if (!isOk) { + throw new ErrSmartContractQuery(queryResponse.returnCode, queryResponse.returnMessage); + } + } + createQuery(options: { contract: string; caller?: string; @@ -62,13 +83,7 @@ export class SmartContractQueriesController { } private areArgsOfTypedValue(args: any[]): boolean { - for (const arg of args) { - if (!arg.belongsToTypesystem) { - return false; - } - } - - return true; + return args.every((arg) => isTyped(arg)); } private areArgsBuffers(args: any[]): boolean { diff --git a/src/smartcontracts/codec/binary.spec.ts b/src/smartcontracts/codec/binary.spec.ts index 95d2304d..33df7e6c 100644 --- a/src/smartcontracts/codec/binary.spec.ts +++ b/src/smartcontracts/codec/binary.spec.ts @@ -1,7 +1,49 @@ import * as errors from "../../errors"; import { assert } from "chai"; import { BinaryCodec, BinaryCodecConstraints } from "./binary"; -import { AddressType, AddressValue, BigIntType, BigUIntType, BigUIntValue, BooleanType, BooleanValue, I16Type, I32Type, I64Type, I8Type, NumericalType, NumericalValue, Struct, Field, StructType, TypedValue, U16Type, U32Type, U32Value, U64Type, U64Value, U8Type, U8Value, List, ListType, EnumType, EnumVariantDefinition, EnumValue, ArrayVec, ArrayVecType, U16Value, TokenIdentifierType, TokenIdentifierValue, StringValue, StringType, BigIntValue, I64Value, I32Value, I16Value, I8Value } from "../typesystem"; +import { + AddressType, + AddressValue, + BigIntType, + BigUIntType, + BigUIntValue, + BooleanType, + BooleanValue, + I16Type, + I32Type, + I64Type, + I8Type, + NumericalType, + NumericalValue, + Struct, + Field, + StructType, + TypedValue, + U16Type, + U32Type, + U32Value, + U64Type, + U64Value, + U8Type, + U8Value, + List, + ListType, + EnumType, + EnumVariantDefinition, + EnumValue, + ArrayVec, + ArrayVecType, + U16Value, + TokenIdentifierType, + TokenIdentifierValue, + StringValue, + StringType, + BigIntValue, + I64Value, + I32Value, + I16Value, + I8Value, +} from "../typesystem"; import { isMsbOne } from "./utils"; import { Address } from "../../address"; import { BytesType, BytesValue } from "../typesystem/bytes"; @@ -24,47 +66,55 @@ describe("test binary codec (basic)", () => { let [decodedNested, nestedLength] = codec.decodeNested(Buffer.from(nested), type); assert.instanceOf(decodedNested, BooleanValue); - assert.isTrue((decodedNested).equals(value)); + assert.isTrue(decodedNested.equals(value)); assert.equal(nestedLength, 1); let decodedTop = codec.decodeTopLevel(Buffer.from(topLevel), type); assert.instanceOf(decodedTop, BooleanValue); - assert.isTrue((decodedTop).equals(value)); + assert.isTrue(decodedTop.equals(value)); } }); it("should create numeric values, encode and decode", async () => { - // Small int - checkNumerical(BigInt(42), new U8Type(), [0x2A], [0x2A]); - checkNumerical(BigInt(42), new U16Type(), [0x00, 0x2A], [0x2A]); - checkNumerical(BigInt(42), new U64Type(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A], [0x2A]); - checkNumerical(BigInt(-10), new I8Type(), [0xF6], [0xF6]); - checkNumerical(BigInt(-10), new I16Type(), [0xFF, 0xF6], [0xF6]); + checkNumerical(BigInt(42), new U8Type(), [0x2a], [0x2a]); + checkNumerical(BigInt(42), new U16Type(), [0x00, 0x2a], [0x2a]); + checkNumerical(BigInt(42), new U64Type(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a], [0x2a]); + checkNumerical(BigInt(-10), new I8Type(), [0xf6], [0xf6]); + checkNumerical(BigInt(-10), new I16Type(), [0xff, 0xf6], [0xf6]); // BigInt checkNumerical(BigInt(0), new BigIntType(), [0, 0, 0, 0], []); checkNumerical(BigInt(1), new BigIntType(), [0, 0, 0, 1, 0x01], [0x01]); - checkNumerical(BigInt(-1), new BigIntType(), [0, 0, 0, 1, 0xFF], [0xFF]); - checkNumerical(BigInt(-2), new BigIntType(), [0, 0, 0, 1, 0xFE], [0xFE]); - checkNumerical(BigInt(127), new BigIntType(), [0, 0, 0, 1, 0x7F], [0x7F]); + checkNumerical(BigInt(-1), new BigIntType(), [0, 0, 0, 1, 0xff], [0xff]); + checkNumerical(BigInt(-2), new BigIntType(), [0, 0, 0, 1, 0xfe], [0xfe]); + checkNumerical(BigInt(127), new BigIntType(), [0, 0, 0, 1, 0x7f], [0x7f]); checkNumerical(BigInt(128), new BigIntType(), [0, 0, 0, 2, 0x00, 0x80], [0x00, 0x80]); - checkNumerical(BigInt(255), new BigIntType(), [0, 0, 0, 2, 0x00, 0xFF], [0x00, 0xFF]); + checkNumerical(BigInt(255), new BigIntType(), [0, 0, 0, 2, 0x00, 0xff], [0x00, 0xff]); checkNumerical(BigInt(256), new BigIntType(), [0, 0, 0, 2, 0x01, 0x00], [0x01, 0x00]); - checkNumerical(BigInt(-255), new BigIntType(), [0, 0, 0, 2, 0xFF, 0x01], [0xFF, 0x01]); - checkNumerical(BigInt(-257), new BigIntType(), [0, 0, 0, 2, 0xFE, 0xFF], [0xFE, 0xFF]); + checkNumerical(BigInt(-255), new BigIntType(), [0, 0, 0, 2, 0xff, 0x01], [0xff, 0x01]); + checkNumerical(BigInt(-257), new BigIntType(), [0, 0, 0, 2, 0xfe, 0xff], [0xfe, 0xff]); // Zero, fixed-size - [new U8Type(), new I8Type(), new U16Type(), new I16Type(), new U32Type(), new I32Type(), new U64Type(), new I64Type()].forEach(type => { + [ + new U8Type(), + new I8Type(), + new U16Type(), + new I16Type(), + new U32Type(), + new I32Type(), + new U64Type(), + new I64Type(), + ].forEach((type) => { checkNumerical(BigInt(0), type, Array(type.sizeInBytes!).fill(0), []); }); // Zero, arbitrary-size (big) - [new BigIntType(), new BigUIntType()].forEach(type => { + [new BigIntType(), new BigUIntType()].forEach((type) => { checkNumerical(BigInt(0), type, [0, 0, 0, 0], []); }); @@ -92,7 +142,10 @@ describe("test binary codec (basic)", () => { assert.deepEqual(new U16Value("0xabcdef"), new U16Value(BigInt(0xabcdef))); assert.deepEqual(new U8Value("0xabcdef"), new U8Value(BigInt(0xabcdef))); - assert.deepEqual(new BigIntValue(BigInt("0xabcdefabcdefabcdef")), new BigIntValue(BigInt("0xabcdefabcdefabcdef"))); + assert.deepEqual( + new BigIntValue(BigInt("0xabcdefabcdefabcdef")), + new BigIntValue(BigInt("0xabcdefabcdefabcdef")), + ); assert.deepEqual(new I64Value("0xabcdef"), new I64Value(BigInt(0xabcdef))); assert.deepEqual(new I32Value("0xabcdef"), new I32Value(BigInt(0xabcdef))); assert.deepEqual(new I16Value("0xabcdef"), new I16Value(BigInt(0xabcdef))); @@ -108,12 +161,18 @@ describe("test binary codec (basic)", () => { assert.deepEqual(codec.encodeNested(bytesValue), Buffer.from([...length, ...payload])); assert.deepEqual(codec.encodeTopLevel(bytesValue), Buffer.from(payload)); - assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new BytesType()), [bytesValue, 8]); + assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new BytesType()), [ + bytesValue, + 8, + ]); assert.deepEqual(codec.decodeTopLevel(Buffer.from(payload), new BytesType()), bytesValue); assert.deepEqual(codec.encodeNested(stringValue), Buffer.from([...length, ...payload])); assert.deepEqual(codec.encodeTopLevel(stringValue), Buffer.from(payload)); - assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new StringType()), [stringValue, 8]); + assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new StringType()), [ + stringValue, + 8, + ]); assert.deepEqual(codec.decodeTopLevel(Buffer.from(payload), new StringType()), stringValue); }); }); @@ -121,20 +180,21 @@ describe("test binary codec (basic)", () => { describe("test binary codec (advanced)", () => { it("should encode / decode lists", async () => { let codec = new BinaryCodec(); - let list = new List( - new ListType(new AddressType()), - [ - new AddressValue(new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")), - new AddressValue(new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")), - new AddressValue(new Address("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8")) - ]); + let list = new List(new ListType(new AddressType()), [ + new AddressValue(new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")), + new AddressValue(new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")), + new AddressValue(new Address("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8")), + ]); let bufferNested = codec.encodeNested(list); let bufferTopLevel = codec.encodeTopLevel(list); assert.equal(bufferNested.length, 4 + list.getLength() * 32); assert.equal(bufferTopLevel.length, list.getLength() * 32); - let [decodedNested, decodedNestedLength] = codec.decodeNested(bufferNested, new ListType(new AddressType())); + let [decodedNested, decodedNestedLength] = codec.decodeNested( + bufferNested, + new ListType(new AddressType()), + ); let decodedTopLevel = codec.decodeTopLevel(bufferTopLevel, new ListType(new AddressType())); assert.equal(decodedNestedLength, bufferNested.length); assert.equal(decodedNested.getLength(), 3); @@ -156,21 +216,31 @@ describe("test binary codec (advanced)", () => { let list = new List(new ListType(new U32Type()), items); - assert.throws(() => { - codec.encodeNested(list); - }, errors.ErrCodec, `List too large: ${numItems} > ${codec.constraints.maxListLength}`); + assert.throws( + () => { + codec.encodeNested(list); + }, + errors.ErrCodec, + `List too large: ${numItems} > ${codec.constraints.maxListLength}`, + ); - assert.throws(() => { - codec.encodeTopLevel(list); - }, errors.ErrCodec, `List too large: ${numItems} > ${codec.constraints.maxListLength}`); + assert.throws( + () => { + codec.encodeTopLevel(list); + }, + errors.ErrCodec, + `List too large: ${numItems} > ${codec.constraints.maxListLength}`, + ); }); it("benchmark: should work well with large lists", async function () { let numItems = 2 ** 12; - let codec = new BinaryCodec(new BinaryCodecConstraints({ - maxListLength: numItems, - maxBufferLength: numItems * 4 + 4 - })); + let codec = new BinaryCodec( + new BinaryCodecConstraints({ + maxListLength: numItems, + maxBufferLength: numItems * 4 + 4, + }), + ); let items: TypedValue[] = []; @@ -198,10 +268,7 @@ describe("test binary codec (advanced)", () => { let length = 20; let sizeOfItem = 2; // u16 let arrayType = new ArrayVecType(length, new U16Type()); - let array = new ArrayVec( - arrayType, - Array(length).fill(new U16Value(0xABBA)) - ); + let array = new ArrayVec(arrayType, Array(length).fill(new U16Value(0xabba))); let bufferNested = codec.encodeNested(array); let bufferTopLevel = codec.encodeTopLevel(array); @@ -221,18 +288,15 @@ describe("test binary codec (advanced)", () => { it("should encode / decode structs", async () => { let codec = new BinaryCodec(); - let fooType = new StructType( - "Foo", - [ - new FieldDefinition("token_identifier", "", new TokenIdentifierType()), - new FieldDefinition("ticket_price", "", new BigUIntType()), - new FieldDefinition("tickets_left", "", new U32Type()), - new FieldDefinition("deadline", "", new U64Type()), - new FieldDefinition("max_entries_per_user", "", new U32Type()), - new FieldDefinition("prize_distribution", "", new BytesType()), - new FieldDefinition("prize_pool", "", new BigUIntType()) - ] - ); + let fooType = new StructType("Foo", [ + new FieldDefinition("token_identifier", "", new TokenIdentifierType()), + new FieldDefinition("ticket_price", "", new BigUIntType()), + new FieldDefinition("tickets_left", "", new U32Type()), + new FieldDefinition("deadline", "", new U64Type()), + new FieldDefinition("max_entries_per_user", "", new U32Type()), + new FieldDefinition("prize_distribution", "", new BytesType()), + new FieldDefinition("prize_pool", "", new BigUIntType()), + ]); let fooStruct = new Struct(fooType, [ new Field(new TokenIdentifierValue("lucky-token"), "token_identifier"), @@ -241,10 +305,12 @@ describe("test binary codec (advanced)", () => { new Field(new U64Value(new BigNumber("0x000000005fc2b9db")), "deadline"), new Field(new U32Value(0xffffffff), "max_entries_per_user"), new Field(new BytesValue(Buffer.from([0x64])), "prize_distribution"), - new Field(new BigUIntValue(new BigNumber("94720000000000000000000")), "prize_pool") + new Field(new BigUIntValue(new BigNumber("94720000000000000000000")), "prize_pool"), ]); - let encodedExpected = serialized("[0000000b|6c75636b792d746f6b656e] [00000001|01] [00000000] [000000005fc2b9db] [ffffffff] [00000001|64] [0000000a|140ec80fa7ee88000000]"); + let encodedExpected = serialized( + "[0000000b|6c75636b792d746f6b656e] [00000001|01] [00000000] [000000005fc2b9db] [ffffffff] [00000001|64] [0000000a|140ec80fa7ee88000000]", + ); let encoded = codec.encodeNested(fooStruct); assert.deepEqual(encoded, encodedExpected); @@ -260,28 +326,27 @@ describe("test binary codec (advanced)", () => { deadline: new BigNumber("0x000000005fc2b9db", 16), max_entries_per_user: new BigNumber(0xffffffff), prize_distribution: Buffer.from([0x64]), - prize_pool: new BigNumber("94720000000000000000000") + prize_pool: new BigNumber("94720000000000000000000"), }); }); it("should encode / decode structs containing a TokenIdentifier", async () => { let codec = new BinaryCodec(); - let paymentType = new StructType( - "Payment", - [ - new FieldDefinition("token_identifier", "", new TokenIdentifierType()), - new FieldDefinition("nonce", "", new U64Type()), - new FieldDefinition("amount", "", new BigUIntType()), - ] - ); + let paymentType = new StructType("Payment", [ + new FieldDefinition("token_identifier", "", new TokenIdentifierType()), + new FieldDefinition("nonce", "", new U64Type()), + new FieldDefinition("amount", "", new BigUIntType()), + ]); let paymentStruct = new Struct(paymentType, [ new Field(new TokenIdentifierValue("TEST-1234"), "token_identifier"), new Field(new U64Value(new BigNumber(42)), "nonce"), - new Field(new BigUIntValue(new BigNumber("123450000000000000000")), "amount") + new Field(new BigUIntValue(new BigNumber("123450000000000000000")), "amount"), ]); - let encodedExpected = serialized("[00000009|544553542d31323334] [000000000000002a] [00000009|06b13680ef11f90000]"); + let encodedExpected = serialized( + "[00000009|544553542d31323334] [000000000000002a] [00000009|06b13680ef11f90000]", + ); let encoded = codec.encodeNested(paymentStruct); assert.deepEqual(encoded, encodedExpected); @@ -302,29 +367,29 @@ describe("test binary codec (advanced)", () => { let enumType = new EnumType("Colour", [ new EnumVariantDefinition("Orange", 0), new EnumVariantDefinition("Green", 1), - new EnumVariantDefinition("Blue", 255) + new EnumVariantDefinition("Blue", 255), ]); let orange = EnumValue.fromName(enumType, "Orange"); let green = EnumValue.fromName(enumType, "Green"); - let blue = EnumValue.fromName(enumType, "Blue") + let blue = EnumValue.fromName(enumType, "Blue"); assert.deepEqual(codec.encodeNested(orange), Buffer.from([0x00])); assert.deepEqual(codec.encodeTopLevel(orange), Buffer.from([])); assert.deepEqual(codec.encodeNested(green), Buffer.from([0x01])); assert.deepEqual(codec.encodeTopLevel(green), Buffer.from([0x01])); - assert.deepEqual(codec.encodeNested(blue), Buffer.from([0xFF])); - assert.deepEqual(codec.encodeTopLevel(blue), Buffer.from([0xFF])); + assert.deepEqual(codec.encodeNested(blue), Buffer.from([0xff])); + assert.deepEqual(codec.encodeTopLevel(blue), Buffer.from([0xff])); assert.isTrue(orange.equals(codec.decodeTopLevel(Buffer.from([]), enumType))); assert.isTrue(green.equals(codec.decodeTopLevel(Buffer.from([0x01]), enumType))); - assert.isTrue(blue.equals(codec.decodeTopLevel(Buffer.from([0xFF]), enumType))); + assert.isTrue(blue.equals(codec.decodeTopLevel(Buffer.from([0xff]), enumType))); let [decoded] = codec.decodeNested(Buffer.from([0x00]), enumType); assert.deepEqual(decoded, orange); [decoded] = codec.decodeNested(Buffer.from([0x01]), enumType); assert.deepEqual(decoded, green); - [decoded] = codec.decodeNested(Buffer.from([0xFF]), enumType); + [decoded] = codec.decodeNested(Buffer.from([0xff]), enumType); assert.deepEqual(decoded, blue); }); @@ -337,35 +402,40 @@ describe("test binary codec (advanced)", () => { new FieldDefinition("1", "green component", new U8Type()), new FieldDefinition("2", "blue component", new U8Type()), new FieldDefinition("3", "hex code", new BytesType()), - new FieldDefinition("4", "fruits", typeOfListOfStrings) + new FieldDefinition("4", "fruits", typeOfListOfStrings), ]); let blueVariant = new EnumVariantDefinition("Blue", 1, [ new FieldDefinition("0", "hex code", new BytesType()), - new FieldDefinition("1", "fruits", typeOfListOfStrings) + new FieldDefinition("1", "fruits", typeOfListOfStrings), ]); - let enumType = new EnumType("Colour", [ - orangeVariant, - blueVariant - ]); + let enumType = new EnumType("Colour", [orangeVariant, blueVariant]); let orange = new EnumValue(enumType, orangeVariant, [ new Field(new U8Value(255), "0"), new Field(new U8Value(165), "1"), new Field(new U8Value(0), "2"), new Field(BytesValue.fromUTF8("#FFA500"), "3"), - new Field(new List(typeOfListOfStrings, [BytesValue.fromUTF8("orange"), BytesValue.fromUTF8("persimmon")]), "4") + new Field( + new List(typeOfListOfStrings, [BytesValue.fromUTF8("orange"), BytesValue.fromUTF8("persimmon")]), + "4", + ), ]); let blue = new EnumValue(enumType, blueVariant, [ new Field(BytesValue.fromUTF8("#0000FF"), "0"), - new Field(new List(typeOfListOfStrings, [BytesValue.fromUTF8("blueberry"), BytesValue.fromUTF8("plum")]), "1") + new Field( + new List(typeOfListOfStrings, [BytesValue.fromUTF8("blueberry"), BytesValue.fromUTF8("plum")]), + "1", + ), ]); // Orange // [[discriminant = 0]] [R] [G] [B] [bytes for hex code] [list of 2 elements (fruits)] - let orangeEncodedNested = serialized("[[00]] [ff] [a5] [00] [00000007 | 23464641353030] [00000002 | [00000006|6f72616e6765] [00000009|70657273696d6d6f6e]]"); + let orangeEncodedNested = serialized( + "[[00]] [ff] [a5] [00] [00000007 | 23464641353030] [00000002 | [00000006|6f72616e6765] [00000009|70657273696d6d6f6e]]", + ); let orangeEncodedTopLevel = orangeEncodedNested; assert.deepEqual(codec.encodeNested(orange), orangeEncodedNested); assert.deepEqual(codec.encodeTopLevel(orange), orangeEncodedTopLevel); @@ -377,7 +447,9 @@ describe("test binary codec (advanced)", () => { // Blue // [[discriminant = 01]] [bytes for hex code] [list of 2 elements (fruits)] - let blueEncodedNested = serialized("[[01]] [00000007 | 23303030304646] [ 00000002 | [00000009|626c75656265727279] [00000004|706c756d]]"); + let blueEncodedNested = serialized( + "[[01]] [00000007 | 23303030304646] [ 00000002 | [00000009|626c75656265727279] [00000004|706c756d]]", + ); let blueEncodedTopLevel = blueEncodedNested; assert.deepEqual(codec.encodeNested(blue), blueEncodedNested); assert.deepEqual(codec.encodeTopLevel(blue), blueEncodedTopLevel); @@ -397,9 +469,9 @@ function serialized(prettyHex: string): Buffer { describe("test codec utilities", () => { it("should check whether isMsbOne", async () => { - assert.isTrue(isMsbOne(Buffer.from([0xFF]), 0)); - assert.isTrue(isMsbOne(Buffer.from([0x00, 0xFF]), 1)); - assert.isTrue(isMsbOne(Buffer.from([0x00, 0xFF, 0xFF]), 2)); + assert.isTrue(isMsbOne(Buffer.from([0xff]), 0)); + assert.isTrue(isMsbOne(Buffer.from([0x00, 0xff]), 1)); + assert.isTrue(isMsbOne(Buffer.from([0x00, 0xff, 0xff]), 2)); assert.isFalse(isMsbOne(Buffer.from([1]))); assert.isFalse(isMsbOne(Buffer.from([2]))); diff --git a/src/smartcontracts/nativeSerializer.spec.ts b/src/smartcontracts/nativeSerializer.spec.ts index 221f7b05..72df6c90 100644 --- a/src/smartcontracts/nativeSerializer.spec.ts +++ b/src/smartcontracts/nativeSerializer.spec.ts @@ -430,6 +430,34 @@ describe("test native serializer", () => { assert.deepEqual(typedValues[1].valueOf(), []); }); + it("should accept null or undefined for option types and optionals", async () => { + const endpoint = AbiRegistry.create({ + endpoints: [ + { + name: "foo", + inputs: [ + { + type: "Option", + }, + { + type: "optional", + }, + ], + outputs: [], + }, + ], + }).getEndpoint("foo"); + + const typedValuesUsingNull = NativeSerializer.nativeToTypedValues([null, null], endpoint); + const typedValuesUsingUndefined = NativeSerializer.nativeToTypedValues([undefined, undefined], endpoint); + + assert.deepEqual(typedValuesUsingNull, typedValuesUsingUndefined); + assert.deepEqual(typedValuesUsingNull[0].getType(), new OptionType(new NullType())); + assert.deepEqual(typedValuesUsingNull[0].valueOf(), null); + assert.deepEqual(typedValuesUsingNull[1].getType(), new OptionalType(new U32Type())); + assert.deepEqual(typedValuesUsingNull[1].valueOf(), null); + }); + it("should perform type inference (enums)", async () => { const abiRegistry = AbiRegistry.create({ endpoints: [ diff --git a/src/smartcontracts/nativeSerializer.ts b/src/smartcontracts/nativeSerializer.ts index e99ed2db..06f3c6cd 100644 --- a/src/smartcontracts/nativeSerializer.ts +++ b/src/smartcontracts/nativeSerializer.ts @@ -31,6 +31,7 @@ import { I64Value, I8Type, I8Value, + isTyped, List, ListType, NumericalType, @@ -63,6 +64,7 @@ export namespace NativeTypes { export type NativeBuffer = Buffer | string; export type NativeBytes = Buffer | { valueOf(): Buffer } | string; export type NativeAddress = string | Buffer | IAddress | { getAddress(): IAddress }; + export type NativeBigNumber = BigNumber.Value | bigint; } export namespace NativeSerializer { @@ -168,7 +170,7 @@ export namespace NativeSerializer { } function convertToTypedValue(value: any, type: Type, errorContext: ArgumentErrorContext): TypedValue { - if (value && value.belongsToTypesystem) { + if (value && isTyped(value)) { // Value is already typed, no need to convert it. return value; } @@ -284,7 +286,7 @@ export namespace NativeSerializer { function toPrimitive(native: any, type: Type, errorContext: ArgumentErrorContext): TypedValue { if (type instanceof NumericalType) { - let number = new BigNumber(native); + const number = new BigNumber(native); return convertNumericalType(number, type, errorContext); } if (type instanceof BytesType) { @@ -389,7 +391,11 @@ export namespace NativeSerializer { } // TODO: move logic to typesystem/numerical.ts - function convertNumericalType(number: BigNumber.Value, type: Type, errorContext: ArgumentErrorContext): TypedValue { + function convertNumericalType( + number: NativeTypes.NativeBigNumber, + type: Type, + errorContext: ArgumentErrorContext, + ): TypedValue { switch (type.constructor) { case U8Type: return new U8Value(number); diff --git a/src/smartcontracts/smartContract.local.net.spec.ts b/src/smartcontracts/smartContract.local.net.spec.ts index 75040dc0..99219622 100644 --- a/src/smartcontracts/smartContract.local.net.spec.ts +++ b/src/smartcontracts/smartContract.local.net.spec.ts @@ -1,8 +1,14 @@ import { assert } from "chai"; +import { promises } from "fs"; +import { QueryRunnerAdapter } from "../adapters/queryRunnerAdapter"; import { Logger } from "../logger"; +import { SmartContractQueriesController } from "../smartContractQueriesController"; import { prepareDeployment } from "../testutils"; import { createLocalnetProvider } from "../testutils/networkProviders"; import { loadTestWallets, TestWallet } from "../testutils/wallets"; +import { TransactionComputer } from "../transactionComputer"; +import { SmartContractTransactionsFactory } from "../transactionsFactories/smartContractTransactionsFactory"; +import { TransactionsFactoryConfig } from "../transactionsFactories/transactionsFactoryConfig"; import { TransactionWatcher } from "../transactionWatcher"; import { decodeUnsignedNumber } from "./codec"; import { ContractFunction } from "./function"; @@ -10,12 +16,6 @@ import { ResultsParser } from "./resultsParser"; import { SmartContract } from "./smartContract"; import { AddressValue, BigUIntValue, OptionalValue, OptionValue, TokenIdentifierValue, U32Value } from "./typesystem"; import { BytesValue } from "./typesystem/bytes"; -import { TransactionsFactoryConfig } from "../transactionsFactories/transactionsFactoryConfig"; -import { SmartContractTransactionsFactory } from "../transactionsFactories/smartContractTransactionsFactory"; -import { promises } from "fs"; -import { TransactionComputer } from "../transactionComputer"; -import { QueryRunnerAdapter } from "../adapters/queryRunnerAdapter"; -import { SmartContractQueriesController } from "../smartContractQueriesController"; describe("test on local testnet", function () { let alice: TestWallet, bob: TestWallet, carol: TestWallet; @@ -258,6 +258,7 @@ describe("test on local testnet", function () { // Check counter let query = contract.createQuery({ func: new ContractFunction("get") }); let queryResponse = await provider.queryContract(query); + assert.lengthOf(queryResponse.getReturnDataParts(), 1); assert.equal(3, decodeUnsignedNumber(queryResponse.getReturnDataParts()[0])); }); @@ -336,6 +337,7 @@ describe("test on local testnet", function () { }); const queryResponse = await smartContractQueriesController.runQuery(query); + assert.lengthOf(queryResponse.returnDataParts, 1); assert.equal(3, decodeUnsignedNumber(Buffer.from(queryResponse.returnDataParts[0]))); }); @@ -398,6 +400,7 @@ describe("test on local testnet", function () { // Query state, do some assertions let query = contract.createQuery({ func: new ContractFunction("totalSupply") }); let queryResponse = await provider.queryContract(query); + assert.lengthOf(queryResponse.getReturnDataParts(), 1); assert.equal(10000, decodeUnsignedNumber(queryResponse.getReturnDataParts()[0])); query = contract.createQuery({ @@ -501,6 +504,7 @@ describe("test on local testnet", function () { arguments: [], }); let queryResponse = await smartContractQueriesController.runQuery(query); + assert.lengthOf(queryResponse.returnDataParts, 1); assert.equal(10000, decodeUnsignedNumber(Buffer.from(queryResponse.returnDataParts[0]))); query = smartContractQueriesController.createQuery({ diff --git a/src/smartcontracts/typesystem/abiRegistry.ts b/src/smartcontracts/typesystem/abiRegistry.ts index 601b65be..25162de2 100644 --- a/src/smartcontracts/typesystem/abiRegistry.ts +++ b/src/smartcontracts/typesystem/abiRegistry.ts @@ -12,6 +12,7 @@ const interfaceNamePlaceholder = "?"; export class AbiRegistry { readonly name: string; readonly constructorDefinition: EndpointDefinition; + readonly upgradeConstructorDefinition?: EndpointDefinition; readonly endpoints: EndpointDefinition[] = []; readonly customTypes: CustomType[] = []; readonly events: EventDefinition[] = []; @@ -19,12 +20,14 @@ export class AbiRegistry { private constructor(options: { name: string; constructorDefinition: EndpointDefinition; + upgradeConstructorDefinition?: EndpointDefinition; endpoints: EndpointDefinition[]; customTypes: CustomType[]; events?: EventDefinition[]; }) { this.name = options.name; this.constructorDefinition = options.constructorDefinition; + this.upgradeConstructorDefinition = options.upgradeConstructorDefinition; this.endpoints = options.endpoints; this.customTypes = options.customTypes; this.events = options.events || []; @@ -33,18 +36,25 @@ export class AbiRegistry { static create(options: { name?: string; constructor?: any; + upgradeConstructor?: any; endpoints?: any[]; types?: Record; events?: any[]; }): AbiRegistry { const name = options.name || interfaceNamePlaceholder; const constructor = options.constructor || {}; + const upgradeConstructor = options.upgradeConstructor || {}; const endpoints = options.endpoints || []; const types = options.types || {}; const events = options.events || []; // Load arbitrary input parameters into properly-defined objects (e.g. EndpointDefinition and CustomType). const constructorDefinition = EndpointDefinition.fromJSON({ name: "constructor", ...constructor }); + const upgradeConstructorDefinition = EndpointDefinition.fromJSON({ + name: "upgradeConstructor", + ...upgradeConstructor, + }); + const endpointDefinitions = endpoints.map((item) => EndpointDefinition.fromJSON(item)); const customTypes: CustomType[] = []; @@ -65,6 +75,7 @@ export class AbiRegistry { const registry = new AbiRegistry({ name: name, constructorDefinition: constructorDefinition, + upgradeConstructorDefinition: upgradeConstructorDefinition, endpoints: endpointDefinitions, customTypes: customTypes, events: eventDefinitions, @@ -139,8 +150,11 @@ export class AbiRegistry { throw new errors.ErrTypingSystem("Did not re-map all custom types"); } - // Let's remap the constructor: + // Let's remap the constructor(s): const newConstructor = mapEndpoint(this.constructorDefinition, mapper); + const newUpgradeConstructor = this.upgradeConstructorDefinition + ? mapEndpoint(this.upgradeConstructorDefinition, mapper) + : undefined; // Then, remap types of all endpoint parameters. // The mapper learned all necessary types in the previous step. @@ -156,6 +170,7 @@ export class AbiRegistry { const newRegistry = new AbiRegistry({ name: this.name, constructorDefinition: newConstructor, + upgradeConstructorDefinition: newUpgradeConstructor, endpoints: newEndpoints, customTypes: newCustomTypes, events: newEvents, diff --git a/src/smartcontracts/typesystem/factory.spec.ts b/src/smartcontracts/typesystem/factory.spec.ts index afe82de1..8645a16c 100644 --- a/src/smartcontracts/typesystem/factory.spec.ts +++ b/src/smartcontracts/typesystem/factory.spec.ts @@ -10,7 +10,7 @@ describe("test factory", () => { let addresses = [ new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), new Address("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"), - new Address("erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan") + new Address("erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan"), ]; let list = createListOfAddresses(addresses); diff --git a/src/smartcontracts/typesystem/variadic.ts b/src/smartcontracts/typesystem/variadic.ts index b054032b..f4f21710 100644 --- a/src/smartcontracts/typesystem/variadic.ts +++ b/src/smartcontracts/typesystem/variadic.ts @@ -28,8 +28,8 @@ export class CountedVariadicType extends Type { /** * An abstraction that represents a sequence of values held under the umbrella of a variadic input / output parameter. - * - * Since at the time of constructing input parameters or decoding output parameters, the length is known, + * + * Since at the time of constructing input parameters or decoding output parameters, the length is known, * this TypedValue behaves similar to a List. */ export class VariadicValue extends TypedValue { @@ -37,7 +37,7 @@ export class VariadicValue extends TypedValue { private readonly items: TypedValue[]; /** - * + * * @param type the type of this TypedValue (an instance of VariadicType), not the type parameter of the VariadicType * @param items the items, having the type type.getFirstTypeParameter() */ @@ -75,7 +75,7 @@ export class VariadicValue extends TypedValue { } valueOf(): any[] { - return this.items.map(item => item.valueOf()); + return this.items.map((item) => item.valueOf()); } equals(other: VariadicValue): boolean { diff --git a/src/testdata/adder.abi.json b/src/testdata/adder.abi.json index 7aba85f5..fd809ce9 100644 --- a/src/testdata/adder.abi.json +++ b/src/testdata/adder.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.71.0-nightly", - "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", - "commitDate": "2023-05-25", + "version": "1.76.0-nightly", + "commitHash": "d86d65bbc19b928387f68427fcc3a0da498d8a19", + "commitDate": "2023-12-10", "channel": "Nightly", - "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" + "short": "rustc 1.76.0-nightly (d86d65bbc 2023-12-10)" }, "contractCrate": { "name": "adder", "version": "0.0.0", - "gitVersion": "v0.45.2.1-reproducible-169-g37d970c" + "gitVersion": "v0.50.1-3-gbed74682a" }, "framework": { "name": "multiversx-sc", - "version": "0.47.2" + "version": "0.50.1" } }, "docs": [ @@ -31,6 +31,15 @@ ], "outputs": [] }, + "upgradeConstructor": { + "inputs": [ + { + "name": "initial_value", + "type": "BigUint" + } + ], + "outputs": [] + }, "endpoints": [ { "name": "getSum", @@ -43,25 +52,9 @@ ] }, { - "name": "upgrade", - "mutability": "mutable", - "inputs": [ - { - "name": "new_value", - "type": "BigUint" - } - ], - "outputs": [] - }, - { - "docs": [ - "Add desired amount to the storage variable." - ], + "docs": ["Add desired amount to the storage variable."], "name": "add", "mutability": "mutable", - "payableInTokens": [ - "*" - ], "inputs": [ { "name": "value", diff --git a/src/testutils/contractController.ts b/src/testutils/contractController.ts index 517d8eef..f72d3079 100644 --- a/src/testutils/contractController.ts +++ b/src/testutils/contractController.ts @@ -16,14 +16,18 @@ export class ContractController { this.parser = new ResultsParser(); this.provider = provider; this.transactionCompletionAwaiter = new TransactionWatcher({ - getTransaction: async (hash: string) => { return await provider.getTransaction(hash, true) } + getTransaction: async (hash: string) => { + return await provider.getTransaction(hash, true); + }, }); } - async deploy(transaction: Transaction): Promise<{ transactionOnNetwork: ITransactionOnNetwork, bundle: UntypedOutcomeBundle }> { + async deploy( + transaction: Transaction, + ): Promise<{ transactionOnNetwork: ITransactionOnNetwork; bundle: UntypedOutcomeBundle }> { const txHash = await this.provider.sendTransaction(transaction); Logger.info(`ContractController.deploy [begin]: transaction = ${txHash}`); - + let transactionOnNetwork = await this.transactionCompletionAwaiter.awaitCompleted(txHash); let bundle = this.parser.parseUntypedOutcome(transactionOnNetwork); @@ -31,16 +35,23 @@ export class ContractController { return { transactionOnNetwork, bundle }; } - async execute(interaction: Interaction, transaction: Transaction): Promise<{ transactionOnNetwork: ITransactionOnNetwork, bundle: TypedOutcomeBundle }> { + async execute( + interaction: Interaction, + transaction: Transaction, + ): Promise<{ transactionOnNetwork: ITransactionOnNetwork; bundle: TypedOutcomeBundle }> { const txHash = await this.provider.sendTransaction(transaction); - Logger.info(`ContractController.execute [begin]: function = ${interaction.getFunction()}, transaction = ${txHash}`); + Logger.info( + `ContractController.execute [begin]: function = ${interaction.getFunction()}, transaction = ${txHash}`, + ); interaction.check(); let transactionOnNetwork = await this.transactionCompletionAwaiter.awaitCompleted(txHash); let bundle = this.parser.parseOutcome(transactionOnNetwork, interaction.getEndpoint()); - Logger.info(`ContractController.execute [end]: function = ${interaction.getFunction()}, transaction = ${txHash}, return code = ${bundle.returnCode}`); + Logger.info( + `ContractController.execute [end]: function = ${interaction.getFunction()}, transaction = ${txHash}, return code = ${bundle.returnCode}`, + ); return { transactionOnNetwork, bundle }; } @@ -52,7 +63,9 @@ export class ContractController { let queryResponse = await this.provider.queryContract(interaction.buildQuery()); let bundle = this.parser.parseQueryResponse(queryResponse, interaction.getEndpoint()); - Logger.debug(`ContractController.query [end]: function = ${interaction.getFunction()}, return code = ${bundle.returnCode}`); + Logger.debug( + `ContractController.query [end]: function = ${interaction.getFunction()}, return code = ${bundle.returnCode}`, + ); return bundle; } } diff --git a/src/testutils/mockNetworkProvider.ts b/src/testutils/mockNetworkProvider.ts index 4a842e0c..a8e0a181 100644 --- a/src/testutils/mockNetworkProvider.ts +++ b/src/testutils/mockNetworkProvider.ts @@ -35,9 +35,15 @@ export class MockNetworkProvider { this.transactions = new Map(); this.accounts = new Map(); - this.accounts.set(MockNetworkProvider.AddressOfAlice.bech32(), { nonce: 0, balance: createAccountBalance(1000) }); + this.accounts.set(MockNetworkProvider.AddressOfAlice.bech32(), { + nonce: 0, + balance: createAccountBalance(1000), + }); this.accounts.set(MockNetworkProvider.AddressOfBob.bech32(), { nonce: 5, balance: createAccountBalance(500) }); - this.accounts.set(MockNetworkProvider.AddressOfCarol.bech32(), { nonce: 42, balance: createAccountBalance(300) }); + this.accounts.set(MockNetworkProvider.AddressOfCarol.bech32(), { + nonce: 42, + balance: createAccountBalance(300), + }); } mockUpdateAccount(address: Address, mutate: (item: IAccountOnNetwork) => void) { diff --git a/src/testutils/wallets.ts b/src/testutils/wallets.ts index e86ce05c..1e67da63 100644 --- a/src/testutils/wallets.ts +++ b/src/testutils/wallets.ts @@ -54,10 +54,11 @@ export async function loadPassword(): Promise { } export async function loadTestWallet(name: string): Promise { - let jsonContents = JSON.parse(await readTestWalletFileContents(name + ".json")); - let pemContents = await readTestWalletFileContents(name + ".pem"); - let pemKey = UserSecretKey.fromPem(pemContents); - return new TestWallet(new Address(jsonContents.address), pemKey.hex(), jsonContents, pemContents); + const jsonContents = JSON.parse(await readTestWalletFileContents(name + ".json")); + const pemContents = await readTestWalletFileContents(name + ".pem"); + const secretKey = UserSecretKey.fromPem(pemContents); + const publicKey = secretKey.generatePublicKey().valueOf(); + return new TestWallet(new Address(publicKey), secretKey.hex(), jsonContents, pemContents); } async function readTestWalletFileContents(name: string): Promise { diff --git a/src/tokenOperations/tokenOperationsFactory.spec.ts b/src/tokenOperations/tokenOperationsFactory.spec.ts index 44c39761..f4e97ac6 100644 --- a/src/tokenOperations/tokenOperationsFactory.spec.ts +++ b/src/tokenOperations/tokenOperationsFactory.spec.ts @@ -19,13 +19,16 @@ describe("test factory", () => { tokenTicker: "TEST", tokenType: "FNG", numDecimals: 2, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "registerAndSetAllRoles@54455354@54455354@464e47@02") + assert.equal(transaction.getData().toString(), "registerAndSetAllRoles@54455354@54455354@464e47@02"); assert.equal(transaction.getNonce(), 42); assert.equal(transaction.getSender().toString(), frank.address.toString()); - assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -41,13 +44,19 @@ describe("test factory", () => { canChangeOwner: true, canUpgrade: false, canAddSpecialRoles: false, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "issue@4652414e4b@4652414e4b@64@@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365") + assert.equal( + transaction.getData().toString(), + "issue@4652414e4b@4652414e4b@64@@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365", + ); assert.equal(transaction.getNonce(), 42); assert.equal(transaction.getSender().toString(), frank.address.toString()); - assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -62,13 +71,19 @@ describe("test factory", () => { canChangeOwner: true, canUpgrade: false, canAddSpecialRoles: false, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "issueSemiFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365") + assert.equal( + transaction.getData().toString(), + "issueSemiFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365", + ); assert.equal(transaction.getNonce(), 42); assert.equal(transaction.getSender().toString(), frank.address.toString()); - assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -83,13 +98,19 @@ describe("test factory", () => { canChangeOwner: true, canUpgrade: false, canAddSpecialRoles: false, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "issueNonFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365") + assert.equal( + transaction.getData().toString(), + "issueNonFungible@4652414e4b@4652414e4b@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365", + ); assert.equal(transaction.getNonce(), 42); assert.equal(transaction.getSender().toString(), frank.address.toString()); - assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -105,13 +126,19 @@ describe("test factory", () => { canChangeOwner: true, canUpgrade: false, canAddSpecialRoles: false, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "registerMetaESDT@4652414e4b@4652414e4b@0a@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365") + assert.equal( + transaction.getData().toString(), + "registerMetaESDT@4652414e4b@4652414e4b@0a@63616e467265657a65@74727565@63616e57697065@74727565@63616e5061757365@74727565@63616e5472616e736665724e4654437265617465526f6c65@74727565@63616e4368616e67654f776e6572@74727565@63616e55706772616465@66616c7365@63616e4164645370656369616c526f6c6573@66616c7365", + ); assert.equal(transaction.getNonce(), 42); assert.equal(transaction.getSender().toString(), frank.address.toString()); - assert.equal(transaction.getReceiver().toString(), "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -124,13 +151,19 @@ describe("test factory", () => { addRoleNFTUpdateAttributes: true, addRoleNFTAddURI: true, addRoleESDTTransferRole: false, - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "setSpecialRole@4652414e4b2d313163653365@1e8a8b6b49de5b7be10aaa158a5a6a4abb4b56cc08f524bb5e6cd5f211ad3e13@45534454526f6c654e4654437265617465@45534454526f6c654e465455706461746541747472696275746573@45534454526f6c654e4654416464555249"); + 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"); + assert.equal( + transaction.getReceiver().toString(), + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + ); }); it("should create ", () => { @@ -143,10 +176,13 @@ describe("test factory", () => { hash: "abba", attributes: Buffer.from("test"), uris: ["a", "b"], - transactionNonce: 42 + transactionNonce: 42, }); - assert.equal(transaction.getData().toString(), "ESDTNFTCreate@4652414e4b2d616139653864@01@74657374@03e8@61626261@74657374@61@62"); + 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/tokenOperationsFactoryConfig.ts b/src/tokenOperations/tokenOperationsFactoryConfig.ts index e1ebb54d..5a346685 100644 --- a/src/tokenOperations/tokenOperationsFactoryConfig.ts +++ b/src/tokenOperations/tokenOperationsFactoryConfig.ts @@ -25,7 +25,7 @@ export class TokenOperationsFactoryConfig { gasLimitStorePerByte: IGasLimit = 50000; issueCost: BigNumber.Value = "50000000000000000"; esdtContractAddress: IAddress = Address.fromBech32( - "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u" + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", ); constructor(chainID: IChainID) { diff --git a/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts b/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts index edabb2ae..ec3c5077 100644 --- a/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts +++ b/src/tokenOperations/tokenOperationsOutcomeParser.spec.ts @@ -21,10 +21,10 @@ describe("test parsers", () => { address: frank.address, identifier: "issue", topics: [createTopic(Buffer.from("FOOBAR"))], - data: "" - } - ] - } + data: "", + }, + ], + }, }); assert.equal(outcome.tokenIdentifier, "FOOBAR"); @@ -44,12 +44,12 @@ describe("test parsers", () => { createTopic(Buffer.from("")), createTopic(Buffer.from("")), createTopic(Buffer.from("ESDTRoleLocalMint")), - createTopic(Buffer.from("ESDTRoleLocalBurn")) + createTopic(Buffer.from("ESDTRoleLocalBurn")), ], - data: "" - } - ] - } + data: "", + }, + ], + }, }); assert.equal(outcome.tokenIdentifier, "FOOBAR"); @@ -71,10 +71,10 @@ describe("test parsers", () => { createTopic(Buffer.from("")), createTopic(bigIntToBuffer("200")), ], - data: "" - } - ] - } + data: "", + }, + ], + }, }); assert.equal(outcome.tokenIdentifier, "FOOBAR"); @@ -97,10 +97,10 @@ describe("test parsers", () => { createTopic(bigIntToBuffer("42")), createTopic(bigIntToBuffer("1")), ], - data: "" - } - ] - } + data: "", + }, + ], + }, }); assert.equal(outcome.tokenIdentifier, "FOOBAR"); @@ -110,7 +110,7 @@ describe("test parsers", () => { function createTopic(value: Buffer): any { return { - valueOf: () => value + valueOf: () => value, }; } }); diff --git a/src/transaction.spec.ts b/src/transaction.spec.ts index c514a3ab..6775fbb9 100644 --- a/src/transaction.spec.ts +++ b/src/transaction.spec.ts @@ -1,3 +1,4 @@ +import { UserPublicKey, UserVerifier } from "@multiversx/sdk-wallet"; import BigNumber from "bignumber.js"; import { assert } from "chai"; import { Address } from "./address"; @@ -9,7 +10,6 @@ import { TokenTransfer } from "./tokens"; import { Transaction } from "./transaction"; import { TransactionComputer } from "./transactionComputer"; import { TransactionPayload } from "./transactionPayload"; -import { UserPublicKey, UserVerifier } from "@multiversx/sdk-wallet/out"; describe("test transaction", async () => { let wallets: Record; @@ -674,10 +674,6 @@ describe("test transaction", async () => { chainID: "", }); - assert.throws(() => { - transactionComputer.computeBytesForSigning(transaction); - }, "Invalid `sender` field. Should be the bech32 address of the sender."); - transaction.sender = wallets.alice.address.toBech32(); assert.throws(() => { diff --git a/src/transactionComputer.ts b/src/transactionComputer.ts index d4117a39..8ff65ff4 100644 --- a/src/transactionComputer.ts +++ b/src/transactionComputer.ts @@ -1,15 +1,14 @@ -import { INetworkConfig } from "./interfaceOfNetwork"; -import * as errors from "./errors"; import BigNumber from "bignumber.js"; -import { ITransaction } from "./interface"; -import { ProtoSerializer } from "./proto"; -import { Transaction } from "./transaction"; import { - BECH32_ADDRESS_LENGTH, MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS, TRANSACTION_OPTIONS_TX_GUARDED, TRANSACTION_OPTIONS_TX_HASH_SIGN, } from "./constants"; +import * as errors from "./errors"; +import { ITransaction } from "./interface"; +import { INetworkConfig } from "./interfaceOfNetwork"; +import { ProtoSerializer } from "./proto"; +import { Transaction } from "./transaction"; const createTransactionHasher = require("blake2b"); const createKeccakHash = require("keccak"); @@ -125,14 +124,6 @@ export class TransactionComputer { } private ensureValidTransactionFields(transaction: ITransaction) { - if (transaction.sender.length !== BECH32_ADDRESS_LENGTH) { - throw new errors.ErrBadUsage("Invalid `sender` field. Should be the bech32 address of the sender."); - } - - if (transaction.receiver.length !== BECH32_ADDRESS_LENGTH) { - throw new errors.ErrBadUsage("Invalid `receiver` field. Should be the bech32 address of the receiver."); - } - if (!transaction.chainID.length) { throw new errors.ErrBadUsage("The `chainID` field is not set"); } diff --git a/src/transactionWatcher.spec.ts b/src/transactionWatcher.spec.ts index 7d957420..fbcfae2e 100644 --- a/src/transactionWatcher.spec.ts +++ b/src/transactionWatcher.spec.ts @@ -4,26 +4,34 @@ import { MarkCompleted, MockNetworkProvider, Wait } from "./testutils"; import { TransactionHash } from "./transaction"; import { TransactionWatcher } from "./transactionWatcher"; - describe("test transactionWatcher", () => { it("should await status == executed using hash", async () => { let hash = new TransactionHash("abbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba"); let provider = new MockNetworkProvider(); let watcher = new TransactionWatcher(provider, { pollingIntervalMilliseconds: 42, - timeoutMilliseconds: 42 * 42 + timeoutMilliseconds: 42 * 42, }); let dummyTransaction = { - getHash: () => hash - } + getHash: () => hash, + }; - provider.mockPutTransaction(hash, new TransactionOnNetwork({ - status: new TransactionStatus("unknown") - })); + provider.mockPutTransaction( + hash, + new TransactionOnNetwork({ + status: new TransactionStatus("unknown"), + }), + ); await Promise.all([ - provider.mockTransactionTimelineByHash(hash, [new Wait(40), new TransactionStatus("pending"), new Wait(40), new TransactionStatus("executed"), new MarkCompleted()]), - watcher.awaitCompleted(dummyTransaction.getHash().hex()) + provider.mockTransactionTimelineByHash(hash, [ + new Wait(40), + new TransactionStatus("pending"), + new Wait(40), + new TransactionStatus("executed"), + new MarkCompleted(), + ]), + watcher.awaitCompleted(dummyTransaction.getHash().hex()), ]); assert.isTrue((await provider.getTransactionStatus(hash.hex())).isExecuted()); @@ -34,19 +42,28 @@ describe("test transactionWatcher", () => { let provider = new MockNetworkProvider(); let watcher = new TransactionWatcher(provider, { pollingIntervalMilliseconds: 42, - timeoutMilliseconds: 42 * 42 + timeoutMilliseconds: 42 * 42, }); let dummyTransaction = { - getHash: () => hash - } + getHash: () => hash, + }; - provider.mockPutTransaction(hash, new TransactionOnNetwork({ - status: new TransactionStatus("unknown") - })); + provider.mockPutTransaction( + hash, + new TransactionOnNetwork({ + status: new TransactionStatus("unknown"), + }), + ); await Promise.all([ - provider.mockTransactionTimelineByHash(hash, [new Wait(40), new TransactionStatus("pending"), new Wait(40), new TransactionStatus("executed"), new MarkCompleted()]), - watcher.awaitCompleted(dummyTransaction) + provider.mockTransactionTimelineByHash(hash, [ + new Wait(40), + new TransactionStatus("pending"), + new Wait(40), + new TransactionStatus("executed"), + new MarkCompleted(), + ]), + watcher.awaitCompleted(dummyTransaction), ]); assert.isTrue((await provider.getTransactionStatus(hash.hex())).isExecuted()); diff --git a/src/transactionsFactories/delegationTransactionsFactory.ts b/src/transactionsFactories/delegationTransactionsFactory.ts index 39027c6d..040d9ac4 100644 --- a/src/transactionsFactories/delegationTransactionsFactory.ts +++ b/src/transactionsFactories/delegationTransactionsFactory.ts @@ -1,5 +1,5 @@ import { Address } from "../address"; -import { DELEGATION_MANAGER_SC_ADDRESS } from "../constants"; +import { DELEGATION_MANAGER_SC_ADDRESS_HEX } from "../constants"; import { Err } from "../errors"; import { IAddress } from "../interface"; import { ArgSerializer, BigUIntValue, BytesValue, StringValue } from "../smartcontracts"; @@ -8,6 +8,7 @@ import { TransactionBuilder } from "./transactionBuilder"; interface IConfig { chainID: string; + addressHrp: string; minGasLimit: bigint; gasLimitPerByte: bigint; gasLimitStake: bigint; @@ -29,10 +30,12 @@ interface IValidatorPublicKey { export class DelegationTransactionsFactory { private readonly config: IConfig; private readonly argSerializer: ArgSerializer; + private readonly delegationManagerAddress: Address; constructor(options: { config: IConfig }) { this.config = options.config; this.argSerializer = new ArgSerializer(); + this.delegationManagerAddress = Address.fromHex(DELEGATION_MANAGER_SC_ADDRESS_HEX, this.config.addressHrp); } createTransactionForNewDelegationContract(options: { @@ -55,7 +58,7 @@ export class DelegationTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(DELEGATION_MANAGER_SC_ADDRESS), + receiver: this.delegationManagerAddress, dataParts: dataParts, gasLimit: executionGasLimit, addDataMovementGas: true, diff --git a/src/transactionsFactories/smartContractTransactionsFactory.spec.ts b/src/transactionsFactories/smartContractTransactionsFactory.spec.ts index 67887915..d108c103 100644 --- a/src/transactionsFactories/smartContractTransactionsFactory.spec.ts +++ b/src/transactionsFactories/smartContractTransactionsFactory.spec.ts @@ -1,6 +1,5 @@ -import { assert, expect } from "chai"; +import { assert } from "chai"; import { Address } from "../address"; -import { CONTRACT_DEPLOY_ADDRESS } from "../constants"; import { Err } from "../errors"; import { U32Value } from "../smartcontracts"; import { Code } from "../smartcontracts/code"; @@ -12,22 +11,22 @@ import { TransactionsFactoryConfig } from "./transactionsFactoryConfig"; describe("test smart contract transactions factory", function () { const config = new TransactionsFactoryConfig({ chainID: "D" }); - let smartContractFactory: SmartContractTransactionsFactory; + let factory: SmartContractTransactionsFactory; let abiAwareFactory: SmartContractTransactionsFactory; - let adderByteCode: Code; - let abiRegistry: AbiRegistry; + let bytecode: Code; + let abi: AbiRegistry; before(async function () { - smartContractFactory = new SmartContractTransactionsFactory({ + factory = new SmartContractTransactionsFactory({ config: config, }); - adderByteCode = await loadContractCode("src/testdata/adder.wasm"); - abiRegistry = await loadAbiRegistry("src/testdata/adder.abi.json"); + bytecode = await loadContractCode("src/testdata/adder.wasm"); + abi = await loadAbiRegistry("src/testdata/adder.abi.json"); abiAwareFactory = new SmartContractTransactionsFactory({ config: config, - abi: abiRegistry, + abi: abi, }); }); @@ -38,9 +37,9 @@ describe("test smart contract transactions factory", function () { assert.throws( () => - smartContractFactory.createTransactionForDeploy({ + factory.createTransactionForDeploy({ sender: sender, - bytecode: adderByteCode.valueOf(), + bytecode: bytecode.valueOf(), gasLimit: gasLimit, arguments: args, }), @@ -52,25 +51,25 @@ describe("test smart contract transactions factory", function () { it("should create 'Transaction' for deploy", async function () { const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const gasLimit = 6000000n; - const args = [new U32Value(0)]; + const args = [new U32Value(1)]; - const transaction = smartContractFactory.createTransactionForDeploy({ + const transaction = factory.createTransactionForDeploy({ sender: sender, - bytecode: adderByteCode.valueOf(), + bytecode: bytecode.valueOf(), gasLimit: gasLimit, arguments: args, }); const transactionAbiAware = abiAwareFactory.createTransactionForDeploy({ sender: sender, - bytecode: adderByteCode.valueOf(), + bytecode: bytecode.valueOf(), gasLimit: gasLimit, arguments: args, }); assert.equal(transaction.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - assert.equal(transaction.receiver, CONTRACT_DEPLOY_ADDRESS); - expect(transaction.data.length).to.be.greaterThan(0); + assert.equal(transaction.receiver, "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"); + assert.deepEqual(transaction.data, Buffer.from(`${bytecode}@0500@0504@01`)); assert.equal(transaction.gasLimit.valueOf(), gasLimit); assert.equal(transaction.value, 0n); @@ -84,7 +83,7 @@ describe("test smart contract transactions factory", function () { const gasLimit = 6000000n; const args = [new U32Value(7)]; - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -116,7 +115,7 @@ describe("test smart contract transactions factory", function () { const gasLimit = 6000000n; const egldAmount = 1000000000000000000n; - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -152,7 +151,7 @@ describe("test smart contract transactions factory", function () { const token = new Token({ identifier: "FOO-6ce17b", nonce: 0n }); const transfer = new TokenTransfer({ token, amount: 10n }); - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -191,7 +190,7 @@ describe("test smart contract transactions factory", function () { const barToken = new Token({ identifier: "BAR-5bc08f", nonce: 0n }); const barTransfer = new TokenTransfer({ token: barToken, amount: 3140n }); - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -235,7 +234,7 @@ describe("test smart contract transactions factory", function () { const token = new Token({ identifier: "NFT-123456", nonce: 1n }); const transfer = new TokenTransfer({ token, amount: 1n }); - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -282,7 +281,7 @@ describe("test smart contract transactions factory", function () { const secondToken = new Token({ identifier: "NFT-123456", nonce: 42n }); const secondTransfer = new TokenTransfer({ token: secondToken, amount: 1n }); - const transaction = smartContractFactory.createTransactionForExecute({ + const transaction = factory.createTransactionForExecute({ sender: sender, contract: contract, function: func, @@ -323,10 +322,10 @@ describe("test smart contract transactions factory", function () { const gasLimit = 6000000n; const args = [new U32Value(7)]; - const transaction = smartContractFactory.createTransactionForUpgrade({ + const transaction = factory.createTransactionForUpgrade({ sender: sender, contract: contract, - bytecode: adderByteCode.valueOf(), + bytecode: bytecode.valueOf(), gasLimit: gasLimit, arguments: args, }); @@ -334,46 +333,125 @@ describe("test smart contract transactions factory", function () { const transactionAbiAware = abiAwareFactory.createTransactionForUpgrade({ sender: sender, contract: contract, - bytecode: adderByteCode.valueOf(), + bytecode: bytecode.valueOf(), gasLimit: gasLimit, arguments: args, }); assert.equal(transaction.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); assert.equal(transaction.receiver, "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); - assert.equal(Buffer.from(transaction.data!).toString(), `upgradeContract@${adderByteCode}@0504@07`); + assert.deepEqual(transaction.data!, Buffer.from(`upgradeContract@${bytecode}@0504@07`)); assert.equal(transaction.gasLimit, gasLimit); assert.equal(transaction.value, 0n); assert.deepEqual(transaction, transactionAbiAware); }); - it("should create 'Transaction' for upgrade, when ABI is available, but it doesn't contain a definition for 'upgrade'", async function () { - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - // Remove all endpoints (for the sake of the test). - abi.endpoints.length = 0; + it("should create 'Transaction' for upgrade, when ABI is available (with fallbacks)", async function () { + const abi = AbiRegistry.create({ + upgradeConstructor: { + inputs: [ + { + type: "u32", + }, + { + type: "u32", + }, + { + type: "u32", + }, + ], + }, + endpoints: [ + { + name: "upgrade", + inputs: [ + { + type: "u32", + }, + { + type: "u32", + }, + ], + }, + ], + constructor: { + inputs: [ + { + type: "u32", + }, + ], + }, + }); const factory = new SmartContractTransactionsFactory({ config: config, abi: abi, }); - const transaction = factory.createTransactionForUpgrade({ - sender: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), - contract: Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"), - bytecode: adderByteCode.valueOf(), - gasLimit: 6000000n, - arguments: [new U32Value(7)], + const bytecode = Buffer.from("abba", "hex"); + const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const receiver = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); + const gasLimit = 6000000n; + + // By default, use the upgrade constructor. + const tx1 = factory.createTransactionForUpgrade({ + sender: sender, + contract: receiver, + bytecode: bytecode, + gasLimit: gasLimit, + arguments: [42, 42, 42], + }); + + assert.equal(Buffer.from(tx1.data!).toString(), `upgradeContract@abba@0504@2a@2a@2a`); + + // Fallback to the "upgrade" endpoint. + (abi).upgradeConstructorDefinition = undefined; + + const tx2 = factory.createTransactionForUpgrade({ + sender: sender, + contract: receiver, + bytecode: bytecode, + gasLimit: gasLimit, + arguments: [42, 42], }); - assert.equal(Buffer.from(transaction.data!).toString(), `upgradeContract@${adderByteCode}@0504@07`); + assert.equal(Buffer.from(tx2.data!).toString(), `upgradeContract@abba@0504@2a@2a`); + + // Fallback to the constructor. + (abi).endpoints.length = 0; + + const tx3 = factory.createTransactionForUpgrade({ + sender: sender, + contract: receiver, + bytecode: bytecode, + gasLimit: gasLimit, + arguments: [42], + }); + + assert.equal(Buffer.from(tx3.data!).toString(), `upgradeContract@abba@0504@2a`); + + // No fallbacks. + (abi).constructorDefinition = undefined; + + assert.throws( + () => + factory.createTransactionForUpgrade({ + sender: sender, + contract: receiver, + bytecode: bytecode, + gasLimit: gasLimit, + arguments: [42], + }), + "Can't convert args to TypedValues", + ); }); it("should create 'Transaction' for claiming developer rewards", async function () { const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); - const transaction = smartContractFactory.createTransactionForClaimingDeveloperRewards({ + const transaction = factory.createTransactionForClaimingDeveloperRewards({ sender: sender, contract: contract, }); @@ -390,7 +468,7 @@ describe("test smart contract transactions factory", function () { const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4"); const newOwner = Address.fromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = smartContractFactory.createTransactionForChangingOwnerAddress({ + const transaction = factory.createTransactionForChangingOwnerAddress({ sender: sender, contract: contract, newOwner: newOwner, diff --git a/src/transactionsFactories/smartContractTransactionsFactory.ts b/src/transactionsFactories/smartContractTransactionsFactory.ts index e6982c7d..c439139f 100644 --- a/src/transactionsFactories/smartContractTransactionsFactory.ts +++ b/src/transactionsFactories/smartContractTransactionsFactory.ts @@ -1,10 +1,11 @@ import { Address } from "../address"; -import { CONTRACT_DEPLOY_ADDRESS, VM_TYPE_WASM_VM } from "../constants"; +import { CONTRACT_DEPLOY_ADDRESS_HEX, VM_TYPE_WASM_VM } from "../constants"; import { Err, ErrBadUsage } from "../errors"; import { IAddress } from "../interface"; import { Logger } from "../logger"; import { ArgSerializer, CodeMetadata, ContractFunction, EndpointDefinition } from "../smartcontracts"; import { NativeSerializer } from "../smartcontracts/nativeSerializer"; +import { isTyped } from "../smartcontracts/typesystem"; import { TokenComputer, TokenTransfer } from "../tokens"; import { Transaction } from "../transaction"; import { byteArrayToHex, utf8ToHex } from "../utils.codec"; @@ -13,6 +14,7 @@ import { TransactionBuilder } from "./transactionBuilder"; interface IConfig { chainID: string; + addressHrp: string; minGasLimit: bigint; gasLimitPerByte: bigint; gasLimitClaimDeveloperRewards: bigint; @@ -21,6 +23,7 @@ interface IConfig { interface IAbi { constructorDefinition: EndpointDefinition; + upgradeConstructorDefinition?: EndpointDefinition; getEndpoint(name: string | ContractFunction): EndpointDefinition; } @@ -33,12 +36,14 @@ export class SmartContractTransactionsFactory { private readonly abi?: IAbi; private readonly tokenComputer: TokenComputer; private readonly dataArgsBuilder: TokenTransfersDataBuilder; + private readonly contractDeployAddress: Address; constructor(options: { config: IConfig; abi?: IAbi }) { this.config = options.config; this.abi = options.abi; this.tokenComputer = new TokenComputer(); this.dataArgsBuilder = new TokenTransfersDataBuilder(); + this.contractDeployAddress = Address.fromHex(CONTRACT_DEPLOY_ADDRESS_HEX, this.config.addressHrp); } createTransactionForDeploy(options: { @@ -68,7 +73,7 @@ export class SmartContractTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(CONTRACT_DEPLOY_ADDRESS), + receiver: this.contractDeployAddress, dataParts: dataParts, gasLimit: options.gasLimit, addDataMovementGas: false, @@ -171,6 +176,10 @@ export class SmartContractTransactionsFactory { return undefined; } + if (this.abi.upgradeConstructorDefinition) { + return this.abi.upgradeConstructorDefinition; + } + try { return this.abi.getEndpoint("upgrade"); } catch (error) { @@ -228,12 +237,6 @@ export class SmartContractTransactionsFactory { } private areArgsOfTypedValue(args: any[]): boolean { - for (const arg of args) { - if (!arg.belongsToTypesystem) { - return false; - } - } - - return true; + return args.every((arg) => isTyped(arg)); } } diff --git a/src/transactionsFactories/tokenManagementTransactionsFactory.ts b/src/transactionsFactories/tokenManagementTransactionsFactory.ts index ec777e75..18c61f1e 100644 --- a/src/transactionsFactories/tokenManagementTransactionsFactory.ts +++ b/src/transactionsFactories/tokenManagementTransactionsFactory.ts @@ -1,5 +1,5 @@ import { Address } from "../address"; -import { ESDT_CONTRACT_ADDRESS } from "../constants"; +import { ESDT_CONTRACT_ADDRESS_HEX } from "../constants"; import { IAddress } from "../interface"; import { Logger } from "../logger"; import { AddressValue, ArgSerializer, BigUIntValue, BytesValue, StringValue } from "../smartcontracts"; @@ -8,6 +8,7 @@ import { TransactionBuilder } from "./transactionBuilder"; interface IConfig { chainID: string; + addressHrp: string; minGasLimit: bigint; gasLimitPerByte: bigint; gasLimitIssue: bigint; @@ -36,12 +37,14 @@ export class TokenManagementTransactionsFactory { private readonly argSerializer: ArgSerializer; private readonly trueAsString: string; private readonly falseAsString: string; + private readonly esdtContractAddress: Address; constructor(options: { config: IConfig }) { this.config = options.config; this.argSerializer = new ArgSerializer(); this.trueAsString = "true"; this.falseAsString = "false"; + this.esdtContractAddress = Address.fromHex(ESDT_CONTRACT_ADDRESS_HEX, this.config.addressHrp); } createTransactionForIssuingFungible(options: { @@ -83,7 +86,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitIssue, addDataMovementGas: true, @@ -129,7 +132,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitIssue, addDataMovementGas: true, @@ -175,7 +178,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitIssue, addDataMovementGas: true, @@ -223,7 +226,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitIssue, addDataMovementGas: true, @@ -253,7 +256,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitIssue, addDataMovementGas: true, @@ -270,7 +273,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitToggleBurnRoleGlobally, addDataMovementGas: true, @@ -286,7 +289,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitToggleBurnRoleGlobally, addDataMovementGas: true, @@ -312,7 +315,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitSetSpecialRole, addDataMovementGas: true, @@ -340,7 +343,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitSetSpecialRole, addDataMovementGas: true, @@ -382,7 +385,7 @@ export class TokenManagementTransactionsFactory { return new TransactionBuilder({ config: this.config, sender: options.sender, - receiver: Address.fromBech32(ESDT_CONTRACT_ADDRESS), + receiver: this.esdtContractAddress, dataParts: dataParts, gasLimit: this.config.gasLimitSetSpecialRole, addDataMovementGas: true, diff --git a/src/transactionsFactories/transactionsFactoryConfig.ts b/src/transactionsFactories/transactionsFactoryConfig.ts index 5614c13f..a05c187b 100644 --- a/src/transactionsFactories/transactionsFactoryConfig.ts +++ b/src/transactionsFactories/transactionsFactoryConfig.ts @@ -1,4 +1,4 @@ -import { DEFAULT_HRP } from "../constants"; +import { LibraryConfig } from "../config"; export class TransactionsFactoryConfig { chainID: string; @@ -40,7 +40,7 @@ export class TransactionsFactoryConfig { constructor(options: { chainID: string }) { // General-purpose configuration this.chainID = options.chainID; - this.addressHrp = DEFAULT_HRP; + this.addressHrp = LibraryConfig.DefaultAddressHrp; this.minGasLimit = 50000n; this.gasLimitPerByte = 1500n; diff --git a/src/transferTransactionsFactory.spec.ts b/src/transferTransactionsFactory.spec.ts index 66b91fe1..39e6f96d 100644 --- a/src/transferTransactionsFactory.spec.ts +++ b/src/transferTransactionsFactory.spec.ts @@ -14,11 +14,17 @@ describe("test transaction factory", () => { sender: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), receiver: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), data: new TransactionPayload("hello"), - chainID: "D" + chainID: "D", }); - assert.equal(transactionWithData.getSender().bech32(), "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - assert.equal(transactionWithData.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); + assert.equal( + transactionWithData.getSender().bech32(), + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + ); + assert.equal( + transactionWithData.getReceiver().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); assert.equal(transactionWithData.getValue(), "10500000000000000000"); assert.equal(transactionWithData.getGasLimit(), 50000 + 5 * 1500); assert.equal(transactionWithData.getData().toString(), "hello"); @@ -28,11 +34,17 @@ describe("test transaction factory", () => { value: TokenTransfer.egldFromAmount(10.5), sender: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), receiver: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), - chainID: "D" + chainID: "D", }); - assert.equal(transactionWithoutData.getSender().bech32(), "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - assert.equal(transactionWithoutData.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); + assert.equal( + transactionWithoutData.getSender().bech32(), + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + ); + assert.equal( + transactionWithoutData.getReceiver().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); assert.equal(transactionWithoutData.getValue(), "10500000000000000000"); assert.equal(transactionWithoutData.getGasLimit(), 50000); assert.equal(transactionWithoutData.getData().toString(), ""); @@ -44,11 +56,17 @@ describe("test transaction factory", () => { tokenTransfer: TokenTransfer.fungibleFromAmount("TEST-8b028f", "100.00", 2), sender: Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), receiver: Address.fromBech32("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), - chainID: "D" + chainID: "D", }); - assert.equal(transaction.getSender().bech32(), "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); + assert.equal( + transaction.getSender().bech32(), + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + ); + assert.equal( + transaction.getReceiver().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); assert.equal(transaction.getValue(), ""); assert.equal(transaction.getGasLimit(), 50000 + 40 * 1500 + 200000 + 100000); assert.equal(transaction.getData().toString(), "ESDTTransfer@544553542d386230323866@2710"); @@ -60,14 +78,23 @@ describe("test transaction factory", () => { tokenTransfer: TokenTransfer.nonFungible("TEST-38f249", 1), destination: new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), sender: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), - chainID: "D" + chainID: "D", }); - assert.equal(transaction.getSender().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); - assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); + assert.equal( + transaction.getSender().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); + assert.equal( + transaction.getReceiver().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); assert.equal(transaction.getValue(), ""); assert.equal(transaction.getGasLimit(), 50000 + 109 * 1500 + 200000 + 800000); - assert.equal(transaction.getData().toString(), "ESDTNFTTransfer@544553542d333866323439@01@01@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8"); + assert.equal( + transaction.getData().toString(), + "ESDTNFTTransfer@544553542d333866323439@01@01@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", + ); assert.equal(transaction.getChainID(), "D"); }); @@ -75,18 +102,27 @@ describe("test transaction factory", () => { const transaction = factory.createMultiESDTNFTTransfer({ tokenTransfers: [ TokenTransfer.nonFungible("FOO-38f249", 1), - TokenTransfer.fungibleFromAmount("BAR-c80d29", "10.00", 18) + TokenTransfer.fungibleFromAmount("BAR-c80d29", "10.00", 18), ], destination: new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"), sender: new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), - chainID: "D" + chainID: "D", }); - assert.equal(transaction.getSender().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); - assert.equal(transaction.getReceiver().bech32(), "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"); + assert.equal( + transaction.getSender().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); + assert.equal( + transaction.getReceiver().bech32(), + "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", + ); assert.equal(transaction.getValue(), ""); 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.getData().toString(), + "MultiESDTNFTTransfer@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@02@464f4f2d333866323439@01@01@4241522d633830643239@@8ac7230489e80000", + ); assert.equal(transaction.getChainID(), "D"); }); });