diff --git a/CHANGELOG.md b/CHANGELOG.md index 67dc996ef..12a604339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## Unreleased - [Breaking change: adjustements to transaction awaitening and completion, transaction watcher](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/173) + - [Breaking change: simplify network config / improve design - not a singleton anymore](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/176) **Breaking changes** - Removed utility functions: `transaction.awaitExecuted()`, `transaction.awaitPending()`. `TransactionWatcher` should be used directly, instead. @@ -13,6 +14,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - Introduced new functions on `TransactionWatcher`: `awaitCompleted()`, `awaitAllEvents()`, `awaitAnyEvent()` etc. - Removed `Transaction.status` (the one on `TransactionOnNetwork` should be read instead). - `Transaction.getAsOnNetwork()` does not wait for notarization / completion anymore. One should explicitly use the transaction watcher, when needed. + - Removed `NetworkConfig.getDefault()` and `NetworkConfig.sync()`. Instead, one should use `let networkConfig = await provider.getNetworkConfig()`. + - Constructor of `Transaction` now requires `chainID`, as well. + - Added `Interaction.withChainID()` - must be used before calling `buildTransaction()`. ## [10.0.0-beta.3] - [Extract dapp / signing providers to separate repositories](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/170) diff --git a/README.md b/README.md index 332db252c..1354554e7 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,13 @@ For advanced smart contract interaction, using ABIs, please see the following te - [abiRegistry.spec.ts](https://github.com/ElrondNetwork/elrond-sdk-erdjs/tree/main/src/smartcontracts/typesystem/abiRegistry.spec.ts) - [argSerializer.spec.ts](https://github.com/ElrondNetwork/elrond-sdk-erdjs/tree/main/src/smartcontracts/argSerializer.spec.ts) -### Synchronizing network parameters +### Fetching network parameters ``` let provider = new ProxyProvider("https://localhost:7950"); -await NetworkConfig.getDefault().sync(provider); - -console.log(NetworkConfig.getDefault().MinGasPrice); -console.log(NetworkConfig.getDefault().ChainID); +let network = await provider.getNetworkConfig(); +console.log(network.MinGasPrice); +console.log(network.ChainID); ``` ### Synchronizing an account object diff --git a/src/constants.ts b/src/constants.ts index b582f0e15..fef0a968e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,6 @@ const JSONbig = require("json-bigint"); +export const TRANSACTION_MIN_GAS_PRICE = 1000000000; export const TRANSACTION_OPTIONS_DEFAULT = 0; export const TRANSACTION_OPTIONS_TX_HASH_SIGN = 1; export const TRANSACTION_VERSION_DEFAULT = 1; diff --git a/src/interactive.ts b/src/interactive.ts index b9edc598b..8109c9696 100644 --- a/src/interactive.ts +++ b/src/interactive.ts @@ -16,7 +16,6 @@ export async function setupInteractive(providerChoice: string): Promise { - await NetworkConfig.getDefault().sync(provider); let wallets = await loadAndSyncTestWallets(provider); let erdSys = await SystemWrapper.load(provider); return { erdSys, Egld, wallets }; diff --git a/src/networkConfig.ts b/src/networkConfig.ts index ec4db02fb..96daf9c52 100644 --- a/src/networkConfig.ts +++ b/src/networkConfig.ts @@ -1,13 +1,10 @@ import BigNumber from "bignumber.js"; -import { IProvider } from "./interface"; import { GasPrice, GasLimit, TransactionVersion, ChainID, GasPriceModifier } from "./networkParams"; /** * An object holding Network configuration parameters. */ export class NetworkConfig { - private static default: NetworkConfig; - /** * The chain ID. E.g. "1" for the Mainnet. */ @@ -69,26 +66,6 @@ export class NetworkConfig { this.MinTransactionVersion = new TransactionVersion(1); } - /** - * Gets the default configuration object (think of the Singleton pattern). - */ - static getDefault(): NetworkConfig { - if (!NetworkConfig.default) { - NetworkConfig.default = new NetworkConfig(); - } - - return NetworkConfig.default; - } - - /** - * Synchronizes a configuration object by querying the Network, through a {@link IProvider}. - * @param provider The provider to use - */ - async sync(provider: IProvider): Promise { - let fresh: NetworkConfig = await provider.getNetworkConfig(); - Object.assign(this, fresh); - } - /** * Constructs a configuration object from a HTTP response (as returned by the provider). */ diff --git a/src/networkParams.ts b/src/networkParams.ts index 0f18d47d4..9638a13bb 100644 --- a/src/networkParams.ts +++ b/src/networkParams.ts @@ -1,8 +1,6 @@ -import { TransactionPayload } from "./transactionPayload"; -import { NetworkConfig } from "./networkConfig"; import * as errors from "./errors"; -import { Egld } from "./balanceBuilder"; import { + TRANSACTION_MIN_GAS_PRICE, TRANSACTION_OPTIONS_DEFAULT, TRANSACTION_OPTIONS_TX_HASH_SIGN, TRANSACTION_VERSION_DEFAULT, TRANSACTION_VERSION_TX_HASH_SIGN @@ -30,17 +28,8 @@ export class GasPrice { this.value = value; } - toDenominated(): string { - let asBalance = Egld.raw(this.value.toString(10)); - return asBalance.toDenominated(); - } - - /** - * Creates a GasPrice object using the minimum value. - */ static min(): GasPrice { - let value = NetworkConfig.getDefault().MinGasPrice.value; - return new GasPrice(value); + return new GasPrice(TRANSACTION_MIN_GAS_PRICE); } valueOf(): number { @@ -70,27 +59,6 @@ export class GasLimit { this.value = value; } - /** - * Creates a GasLimit object for a value-transfer {@link Transaction}. - */ - static forTransfer(data: TransactionPayload): GasLimit { - let value = NetworkConfig.getDefault().MinGasLimit.value; - - if (data) { - value += NetworkConfig.getDefault().GasPerDataByte * data.length(); - } - - return new GasLimit(value); - } - - /** - * Creates a GasLimit object using the minimum value. - */ - static min(): GasLimit { - let value = NetworkConfig.getDefault().MinGasLimit.value; - return new GasLimit(value); - } - add(other: GasLimit): GasLimit { return new GasLimit(this.value + other.value); } @@ -118,6 +86,10 @@ export class ChainID { this.value = value; } + static unspecified(): ChainID { + return new ChainID("?"); + } + valueOf(): string { return this.value; } diff --git a/src/proto/serializer.spec.ts b/src/proto/serializer.spec.ts index 1c135e7ef..ad16ed2cf 100644 --- a/src/proto/serializer.spec.ts +++ b/src/proto/serializer.spec.ts @@ -11,6 +11,7 @@ import { TransactionPayload } from "../transactionPayload"; describe("serialize transactions", () => { let wallets: Record; let serializer = new ProtoSerializer(); + before(async function () { wallets = await loadTestWallets(); }); @@ -21,7 +22,7 @@ describe("serialize transactions", () => { value: Balance.Zero(), receiver: wallets.bob.address, gasPrice: GasPrice.min(), - gasLimit: GasLimit.min(), + gasLimit: new GasLimit(50000), chainID: new ChainID("local-testnet") }); diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index 8dec908b4..1a6e009eb 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -8,7 +8,6 @@ import { Interaction } from "./interaction"; import { GasLimit } from "../networkParams"; import { ReturnCode } from "./returnCode"; import BigNumber from "bignumber.js"; -import { NetworkConfig } from "../networkConfig"; import { BytesValue } from "./typesystem/bytes"; import { chooseProxyProvider } from "../interactive"; @@ -28,16 +27,15 @@ describe("test smart contract interactor", function () { let contract = new SmartContract({ abi: abi }); let controller = new DefaultSmartContractController(abi, provider); - // Currently, this has to be called before creating any Interaction objects, - // because the Transaction objects created under the hood point to the "default" NetworkConfig. - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy the contract let deployTransaction = contract.deploy({ code: await loadContractCode("src/testdata/answer.wasm"), gasLimit: new GasLimit(3000000), - initArguments: [] + initArguments: [], + chainID: network.ChainID }); deployTransaction.setNonce(alice.account.getNonceThenIncrement()); @@ -45,7 +43,9 @@ describe("test smart contract interactor", function () { let { bundle: { returnCode } } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); - let interaction = contract.methods.getUltimateAnswer().withGasLimit(new GasLimit(3000000)); + let interaction = contract.methods.getUltimateAnswer() + .withGasLimit(new GasLimit(3000000)) + .withChainID(network.ChainID); // Query let queryResponseBundle = await controller.query(interaction); @@ -75,16 +75,15 @@ describe("test smart contract interactor", function () { let contract = new SmartContract({ abi: abi }); let controller = new DefaultSmartContractController(abi, provider); - // Currently, this has to be called before creating any Interaction objects, - // because the Transaction objects created under the hood point to the "default" NetworkConfig. - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy the contract let deployTransaction = contract.deploy({ code: await loadContractCode("src/testdata/counter.wasm"), gasLimit: new GasLimit(3000000), - initArguments: [] + initArguments: [], + chainID: network.ChainID }); deployTransaction.setNonce(alice.account.getNonceThenIncrement()); @@ -93,8 +92,12 @@ describe("test smart contract interactor", function () { assert.isTrue(returnCode.isSuccess()); let getInteraction = contract.methods.get(); - let incrementInteraction = (contract.methods.increment()).withGasLimit(new GasLimit(3000000)); - let decrementInteraction = (contract.methods.decrement()).withGasLimit(new GasLimit(3000000)); + let incrementInteraction = (contract.methods.increment()) + .withGasLimit(new GasLimit(3000000)) + .withChainID(network.ChainID); + let decrementInteraction = (contract.methods.decrement()) + .withGasLimit(new GasLimit(3000000)) + .withChainID(network.ChainID); // Query "get()" let { firstValue: counterValue } = await controller.query(getInteraction); @@ -125,16 +128,15 @@ describe("test smart contract interactor", function () { let contract = new SmartContract({ abi: abi }); let controller = new DefaultSmartContractController(abi, provider); - // Currently, this has to be called before creating any Interaction objects, - // because the Transaction objects created under the hood point to the "default" NetworkConfig. - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy the contract let deployTransaction = contract.deploy({ code: await loadContractCode("src/testdata/lottery-esdt.wasm"), gasLimit: new GasLimit(100000000), - initArguments: [] + initArguments: [], + chainID: network.ChainID }); deployTransaction.setNonce(alice.account.getNonceThenIncrement()); @@ -152,15 +154,21 @@ describe("test smart contract interactor", function () { OptionValue.newMissing(), OptionValue.newMissing(), OptionalValue.newMissing() - ]).withGasLimit(new GasLimit(30000000)); + ]) + .withGasLimit(new GasLimit(30000000)) + .withChainID(network.ChainID); let lotteryStatusInteraction = contract.methods.status([ BytesValue.fromUTF8("lucky") - ]).withGasLimit(new GasLimit(5000000)); + ]) + .withGasLimit(new GasLimit(5000000)) + .withChainID(network.ChainID); let getLotteryInfoInteraction = contract.methods.getLotteryInfo([ BytesValue.fromUTF8("lucky") - ]).withGasLimit(new GasLimit(5000000)); + ]) + .withGasLimit(new GasLimit(5000000)) + .withChainID(network.ChainID); // start() let startTransaction = startInteraction.useThenIncrementNonceOf(alice.account).buildTransaction(); diff --git a/src/smartcontracts/interaction.spec.ts b/src/smartcontracts/interaction.spec.ts index 75e03be8f..ed5c05155 100644 --- a/src/smartcontracts/interaction.spec.ts +++ b/src/smartcontracts/interaction.spec.ts @@ -12,7 +12,7 @@ import { SmartContractAbi } from "./abi"; import { Address } from "../address"; import { assert } from "chai"; import { Interaction } from "./interaction"; -import { GasLimit } from "../networkParams"; +import { ChainID, GasLimit } from "../networkParams"; import { ContractFunction } from "./function"; import { QueryResponse } from "./queryResponse"; import { Nonce } from "../nonce"; @@ -40,7 +40,7 @@ describe("test smart contract interactor", function() { let transaction = interaction .withNonce(new Nonce(7)) .withValue(Balance.egld(1)) - .withGasLimitComponents({ estimatedExecutionComponent: 20000000 }) + .withGasLimitComponents({ minGasLimit: 50000, gasPerDataByte: 1500, estimatedExecutionComponent: 20000000 }) .buildTransaction(); let expectedGasLimit = new GasLimit(50000) @@ -116,7 +116,8 @@ describe("test smart contract interactor", function() { let interaction = contract.methods .getUltimateAnswer() - .withGasLimit(new GasLimit(543210)); + .withGasLimit(new GasLimit(543210)) + .withChainID(new ChainID("T")); assert.equal(contract.getAddress(), dummyAddress); assert.deepEqual(interaction.getFunction(), new ContractFunction("getUltimateAnswer")); diff --git a/src/smartcontracts/interaction.ts b/src/smartcontracts/interaction.ts index 2102afe1c..9abf0bae7 100644 --- a/src/smartcontracts/interaction.ts +++ b/src/smartcontracts/interaction.ts @@ -1,12 +1,11 @@ import { Balance } from "../balance"; -import { GasLimit } from "../networkParams"; +import { ChainID, GasLimit, GasPrice } from "../networkParams"; import { Transaction } from "../transaction"; import { Query } from "./query"; import { ContractFunction } from "./function"; import { Address } from "../address"; import { AddressValue, BigUIntValue, BytesValue, TypedValue, U64Value, U8Value } from "./typesystem"; import { Nonce } from "../nonce"; -import { NetworkConfig } from "../networkConfig"; import { ESDTNFT_TRANSFER_FUNCTION_NAME, ESDT_TRANSFER_FUNCTION_NAME, MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME } from "../constants"; import { Account } from "../account"; import { CallArguments } from "./interface"; @@ -33,7 +32,9 @@ export class Interaction { private nonce: Nonce = new Nonce(0); private value: Balance = Balance.Zero(); - private gasLimit: GasLimit = GasLimit.min(); + private gasLimit: GasLimit = new GasLimit(0); + private gasPrice: GasPrice | undefined = undefined; + private chainID: ChainID = ChainID.unspecified(); private querent: Address = new Address(); private isWithSingleESDTTransfer: boolean = false; @@ -104,10 +105,12 @@ export class Interaction { func: func, // GasLimit will be set using "withGasLimit()". gasLimit: this.gasLimit, + gasPrice: this.gasPrice, args: args, // Value will be set using "withValue()". value: this.value, receiver: receiver, + chainID: this.chainID }); transaction.setNonce(this.nonce); @@ -155,9 +158,9 @@ export class Interaction { return this; } - withGasLimitComponents(args: { minGasLimit?: number, gasPerDataByte?: number, estimatedExecutionComponent: number }): Interaction { - let minGasLimit = args.minGasLimit || NetworkConfig.getDefault().MinGasLimit.valueOf(); - let gasPerDataByte = args.gasPerDataByte || NetworkConfig.getDefault().GasPerDataByte; + withGasLimitComponents(args: { minGasLimit: number, gasPerDataByte: number, estimatedExecutionComponent: number }): Interaction { + let minGasLimit = args.minGasLimit; + let gasPerDataByte = args.gasPerDataByte; let transaction = this.buildTransaction(); let dataLength = transaction.getData().length(); @@ -168,6 +171,11 @@ export class Interaction { return this.withGasLimit(gasLimit); } + withGasPrice(gasPrice: GasPrice): Interaction { + this.gasPrice = gasPrice; + return this; + } + withNonce(nonce: Nonce): Interaction { this.nonce = nonce; return this; @@ -177,6 +185,11 @@ export class Interaction { return this.withNonce(account.getNonceThenIncrement()); } + withChainID(chainID: ChainID): Interaction { + this.chainID = chainID; + return this; + } + /** * Sets the "caller" field on contract queries. */ diff --git a/src/smartcontracts/interface.ts b/src/smartcontracts/interface.ts index 7df4026d6..c6d1c80a7 100644 --- a/src/smartcontracts/interface.ts +++ b/src/smartcontracts/interface.ts @@ -1,6 +1,6 @@ import { Address } from "../address"; import { Balance } from "../balance"; -import { GasLimit } from "../networkParams"; +import { ChainID, GasLimit, GasPrice } from "../networkParams"; import { Transaction } from "../transaction"; import { TransactionOnNetwork } from "../transactionOnNetwork"; import { Code } from "./code"; @@ -42,6 +42,8 @@ export interface DeployArguments { initArguments?: TypedValue[]; value?: Balance; gasLimit: GasLimit; + gasPrice?: GasPrice; + chainID: ChainID; } export interface UpgradeArguments { @@ -50,6 +52,8 @@ export interface UpgradeArguments { initArguments?: TypedValue[]; value?: Balance; gasLimit: GasLimit; + gasPrice?: GasPrice; + chainID: ChainID; } export interface CallArguments { @@ -58,6 +62,8 @@ export interface CallArguments { value?: Balance; gasLimit: GasLimit; receiver?: Address; + gasPrice?: GasPrice; + chainID: ChainID; } export interface QueryArguments { diff --git a/src/smartcontracts/queryResponse.ts b/src/smartcontracts/queryResponse.ts index 847113a88..eb1dcb45a 100644 --- a/src/smartcontracts/queryResponse.ts +++ b/src/smartcontracts/queryResponse.ts @@ -15,7 +15,7 @@ export class QueryResponse { this.returnData = init?.returnData || []; this.returnCode = init?.returnCode || ReturnCode.Unknown; this.returnMessage = init?.returnMessage || ""; - this.gasUsed = init?.gasUsed || GasLimit.min(); + this.gasUsed = init?.gasUsed || new GasLimit(0); } /** diff --git a/src/smartcontracts/smartContract.local.net.spec.ts b/src/smartcontracts/smartContract.local.net.spec.ts index 38b00f339..1e7bed8d3 100644 --- a/src/smartcontracts/smartContract.local.net.spec.ts +++ b/src/smartcontracts/smartContract.local.net.spec.ts @@ -29,14 +29,15 @@ describe("test on local testnet", function () { TransactionWatcher.DefaultPollingInterval = 5000; TransactionWatcher.DefaultTimeout = 50000; - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy let contract = new SmartContract({}); let transactionDeploy = contract.deploy({ code: await loadContractCode("src/testdata/counter.wasm"), - gasLimit: new GasLimit(3000000) + gasLimit: new GasLimit(3000000), + chainID: network.ChainID }); transactionDeploy.setNonce(alice.account.nonce); @@ -47,7 +48,8 @@ describe("test on local testnet", function () { // ++ let transactionIncrement = contract.call({ func: new ContractFunction("increment"), - gasLimit: new GasLimit(3000000) + gasLimit: new GasLimit(3000000), + chainID: network.ChainID }); transactionIncrement.setNonce(alice.account.nonce); @@ -58,12 +60,14 @@ describe("test on local testnet", function () { // Now, let's build a few transactions, to be simulated let simulateOne = contract.call({ func: new ContractFunction("increment"), - gasLimit: new GasLimit(100000) + gasLimit: new GasLimit(100000), + chainID: network.ChainID }); let simulateTwo = contract.call({ func: new ContractFunction("foobar"), - gasLimit: new GasLimit(500000) + gasLimit: new GasLimit(500000), + chainID: network.ChainID }); simulateOne.setNonce(alice.account.nonce); @@ -97,14 +101,15 @@ describe("test on local testnet", function () { TransactionWatcher.DefaultPollingInterval = 5000; TransactionWatcher.DefaultTimeout = 50000; - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy let contract = new SmartContract({}); let transactionDeploy = contract.deploy({ code: await loadContractCode("src/testdata/counter.wasm"), - gasLimit: new GasLimit(3000000) + gasLimit: new GasLimit(3000000), + chainID: network.ChainID }); transactionDeploy.setNonce(alice.account.nonce); @@ -115,7 +120,8 @@ describe("test on local testnet", function () { // ++ let transactionIncrementFirst = contract.call({ func: new ContractFunction("increment"), - gasLimit: new GasLimit(2000000) + gasLimit: new GasLimit(2000000), + chainID: network.ChainID }); transactionIncrementFirst.setNonce(alice.account.nonce); @@ -126,7 +132,8 @@ describe("test on local testnet", function () { // ++ let transactionIncrementSecond = contract.call({ func: new ContractFunction("increment"), - gasLimit: new GasLimit(2000000) + gasLimit: new GasLimit(2000000), + chainID: network.ChainID }); transactionIncrementSecond.setNonce(alice.account.nonce); @@ -154,7 +161,7 @@ describe("test on local testnet", function () { TransactionWatcher.DefaultPollingInterval = 5000; TransactionWatcher.DefaultTimeout = 50000; - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy @@ -162,7 +169,8 @@ describe("test on local testnet", function () { let transactionDeploy = contract.deploy({ code: await loadContractCode("src/testdata/erc20.wasm"), gasLimit: new GasLimit(50000000), - initArguments: [new U32Value(10000)] + initArguments: [new U32Value(10000)], + chainID: network.ChainID }); // The deploy transaction should be signed, so that the address of the contract @@ -175,13 +183,15 @@ describe("test on local testnet", function () { let transactionMintBob = contract.call({ func: new ContractFunction("transferToken"), gasLimit: new GasLimit(9000000), - args: [new AddressValue(bob.address), new U32Value(1000)] + args: [new AddressValue(bob.address), new U32Value(1000)], + chainID: network.ChainID }); let transactionMintCarol = contract.call({ func: new ContractFunction("transferToken"), gasLimit: new GasLimit(9000000), - args: [new AddressValue(carol.address), new U32Value(1500)] + args: [new AddressValue(carol.address), new U32Value(1500)], + chainID: network.ChainID }); // Apply nonces and sign the remaining transactions @@ -233,7 +243,7 @@ describe("test on local testnet", function () { TransactionWatcher.DefaultPollingInterval = 5000; TransactionWatcher.DefaultTimeout = 50000; - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy @@ -241,7 +251,8 @@ describe("test on local testnet", function () { let transactionDeploy = contract.deploy({ code: await loadContractCode("src/testdata/lottery-esdt.wasm"), gasLimit: new GasLimit(50000000), - initArguments: [] + initArguments: [], + chainID: network.ChainID }); // The deploy transaction should be signed, so that the address of the contract @@ -264,7 +275,8 @@ describe("test on local testnet", function () { OptionValue.newMissing(), OptionValue.newMissing(), OptionalValue.newMissing() - ] + ], + chainID: network.ChainID }); // Apply nonces and sign the remaining transactions diff --git a/src/smartcontracts/smartContract.spec.ts b/src/smartcontracts/smartContract.spec.ts index 5835d73b5..769aaef44 100644 --- a/src/smartcontracts/smartContract.spec.ts +++ b/src/smartcontracts/smartContract.spec.ts @@ -3,7 +3,7 @@ import { Address } from "../address"; import { Code } from "./code"; import { Nonce } from "../nonce"; import { SmartContract } from "./smartContract"; -import { GasLimit } from "../networkParams"; +import { ChainID, GasLimit } from "../networkParams"; import { InHyperblock, loadTestWallets, MockProvider, setupUnitTestWatcherTimeouts, TestWallet, Wait } from "../testutils"; import { TransactionStatus } from "../transaction"; import { ContractFunction } from "./function"; @@ -14,6 +14,7 @@ import { TransactionWatcher } from "../transactionWatcher"; describe("test contract", () => { let provider = new MockProvider(); + let chainID = new ChainID("test"); let alice: TestWallet; before(async function () { @@ -39,7 +40,8 @@ describe("test contract", () => { let contract = new SmartContract({}); let deployTransaction = contract.deploy({ code: Code.fromBuffer(Buffer.from([1, 2, 3, 4])), - gasLimit: new GasLimit(1000000) + gasLimit: new GasLimit(1000000), + chainID: chainID }); provider.mockUpdateAccount(alice.address, account => { @@ -82,13 +84,15 @@ describe("test contract", () => { let callTransactionOne = contract.call({ func: new ContractFunction("helloEarth"), args: [new U32Value(5), BytesValue.fromHex("0123")], - gasLimit: new GasLimit(150000) + gasLimit: new GasLimit(150000), + chainID: chainID }); let callTransactionTwo = contract.call({ func: new ContractFunction("helloMars"), args: [new U32Value(5), BytesValue.fromHex("0123")], - gasLimit: new GasLimit(1500000) + gasLimit: new GasLimit(1500000), + chainID: chainID }); await alice.sync(provider); diff --git a/src/smartcontracts/smartContract.ts b/src/smartcontracts/smartContract.ts index 3409db5a8..687730a3f 100644 --- a/src/smartcontracts/smartContract.ts +++ b/src/smartcontracts/smartContract.ts @@ -4,7 +4,7 @@ import { Transaction } from "../transaction"; import { TransactionPayload } from "../transactionPayload"; import { Code } from "./code"; import { CodeMetadata } from "./codeMetadata"; -import { CallArguments, DeployArguments, ISmartContract as ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; +import { CallArguments, DeployArguments, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; import { ArwenVirtualMachine } from "./transactionPayloadBuilders"; import { Nonce } from "../nonce"; import { ContractFunction } from "./function"; @@ -135,7 +135,7 @@ export class SmartContract implements ISmartContract { /** * Creates a {@link Transaction} for deploying the Smart Contract to the Network. */ - deploy({ code, codeMetadata, initArguments, value, gasLimit }: DeployArguments): Transaction { + deploy({ code, codeMetadata, initArguments, value, gasLimit, gasPrice, chainID }: DeployArguments): Transaction { codeMetadata = codeMetadata || new CodeMetadata(); initArguments = initArguments || []; value = value || Balance.Zero(); @@ -150,7 +150,9 @@ export class SmartContract implements ISmartContract { receiver: Address.Zero(), value: value, gasLimit: gasLimit, - data: payload + gasPrice: gasPrice, + data: payload, + chainID: chainID }); this.code = code; @@ -170,7 +172,7 @@ export class SmartContract implements ISmartContract { /** * Creates a {@link Transaction} for upgrading the Smart Contract on the Network. */ - upgrade({ code, codeMetadata, initArguments, value, gasLimit }: UpgradeArguments): Transaction { + upgrade({ code, codeMetadata, initArguments, value, gasLimit, gasPrice, chainID }: UpgradeArguments): Transaction { codeMetadata = codeMetadata || new CodeMetadata(); initArguments = initArguments || []; value = value || Balance.Zero(); @@ -185,7 +187,9 @@ export class SmartContract implements ISmartContract { receiver: this.getAddress(), value: value, gasLimit: gasLimit, - data: payload + gasPrice: gasPrice, + data: payload, + chainID: chainID }); this.code = code; @@ -197,7 +201,7 @@ export class SmartContract implements ISmartContract { /** * Creates a {@link Transaction} for calling (a function of) the Smart Contract. */ - call({ func, args, value, gasLimit, receiver }: CallArguments): Transaction { + call({ func, args, value, gasLimit, receiver, gasPrice, chainID }: CallArguments): Transaction { args = args || []; value = value || Balance.Zero(); @@ -210,7 +214,9 @@ export class SmartContract implements ISmartContract { receiver: receiver ? receiver : this.getAddress(), value: value, gasLimit: gasLimit, - data: payload + gasPrice: gasPrice, + data: payload, + chainID: chainID }); return transaction; diff --git a/src/smartcontracts/smartContractResults.local.net.spec.ts b/src/smartcontracts/smartContractResults.local.net.spec.ts index 0ab6be84d..eebf7ee9a 100644 --- a/src/smartcontracts/smartContractResults.local.net.spec.ts +++ b/src/smartcontracts/smartContractResults.local.net.spec.ts @@ -24,14 +24,15 @@ describe("fetch transactions from local testnet", function () { TransactionWatcher.DefaultPollingInterval = 5000; TransactionWatcher.DefaultTimeout = 50000; - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); // Deploy let contract = new SmartContract({}); let transactionDeploy = contract.deploy({ code: await loadContractCode("src/testdata/counter.wasm"), - gasLimit: new GasLimit(3000000) + gasLimit: new GasLimit(3000000), + chainID: network.ChainID }); transactionDeploy.setNonce(alice.account.nonce); @@ -42,7 +43,8 @@ describe("fetch transactions from local testnet", function () { // ++ let transactionIncrement = contract.call({ func: new ContractFunction("increment"), - gasLimit: new GasLimit(3000000) + gasLimit: new GasLimit(3000000), + chainID: network.ChainID }); transactionIncrement.setNonce(alice.account.nonce); @@ -68,14 +70,4 @@ describe("fetch transactions from local testnet", function () { bundle = resultsParser.parseUntypedOutcome(transactionOnNetwork); assert.isTrue(bundle.returnCode.isSuccess()); }); - - it("ESDT", async function () { - this.timeout(60000); - - TransactionWatcher.DefaultPollingInterval = 5000; - TransactionWatcher.DefaultTimeout = 50000; - - await NetworkConfig.getDefault().sync(provider); - await alice.sync(provider); - }); }); diff --git a/src/smartcontracts/wrapper/contractWrapper.ts b/src/smartcontracts/wrapper/contractWrapper.ts index f21095250..2a421b652 100644 --- a/src/smartcontracts/wrapper/contractWrapper.ts +++ b/src/smartcontracts/wrapper/contractWrapper.ts @@ -101,7 +101,8 @@ export class ContractWrapper extends ChainSendContext { let transaction = this.smartContract.deploy({ code: contractCode, gasLimit: this.context.getGasLimit(), - initArguments: convertedArgs + initArguments: convertedArgs, + chainID: this.context.getChainID() }); return transaction; } @@ -122,8 +123,8 @@ export class ContractWrapper extends ChainSendContext { let { abiPath, wasmPath } = await expandProjectPath(projectPath, filenameHint); let abi = await SmartContractAbi.fromAbiPath(abiPath); let smartContract = new SmartContract({ abi: abi }); - - sendContext = sendContext || new SendContext(provider).logger(new ContractLogger()); + let networkConfig = await provider.getNetworkConfig(); + sendContext = sendContext || new SendContext(provider, networkConfig).logger(new ContractLogger()); return new ContractWrapper(smartContract, abi, wasmPath, sendContext, builtinFunctions); } @@ -207,6 +208,7 @@ export class ContractWrapper extends ChainSendContext { let preparedCall = this.prepareCallWithPayment(endpoint, args); let interaction = this.convertPreparedCallToInteraction(preparedCall); interaction.withGasLimit(this.context.getGasLimit()); + interaction.withChainID(this.context.getChainID()); let transaction = interaction.buildTransaction(); return { transaction, interaction }; } diff --git a/src/smartcontracts/wrapper/sendContext.ts b/src/smartcontracts/wrapper/sendContext.ts index 9eb53913f..4a921686b 100644 --- a/src/smartcontracts/wrapper/sendContext.ts +++ b/src/smartcontracts/wrapper/sendContext.ts @@ -1,10 +1,11 @@ -import { GasLimit } from "../../networkParams"; +import { ChainID, GasLimit } from "../../networkParams"; import { IProvider } from "../../interface"; import { ContractLogger } from "./contractLogger"; import { TestWallet } from "../../testutils"; import { Balance } from "../../balance"; import { Err } from "../../errors"; import { getGasFromValue } from "./systemWrapper"; +import { NetworkConfig } from "../../networkConfig"; /** * Stores contextual information which is needed when preparing a transaction. @@ -15,13 +16,15 @@ export class SendContext { private gas_: GasLimit | null; private logger_: ContractLogger | null; private value_: Balance | null; + private networkConfig: NetworkConfig; - constructor(provider: IProvider) { + constructor(provider: IProvider, networkConfig: NetworkConfig) { this.sender_ = null; this.provider_ = provider; this.gas_ = null; this.logger_ = null; this.value_ = null; + this.networkConfig = networkConfig; } provider(provider: IProvider): this { @@ -81,6 +84,10 @@ export class SendContext { throw new Err("gas limit not set"); } + getChainID(): ChainID { + return this.networkConfig.ChainID; + } + getLogger(): ContractLogger | null { return this.logger_; } diff --git a/src/smartcontracts/wrapper/systemWrapper.ts b/src/smartcontracts/wrapper/systemWrapper.ts index 6498845c3..0cc8e04db 100644 --- a/src/smartcontracts/wrapper/systemWrapper.ts +++ b/src/smartcontracts/wrapper/systemWrapper.ts @@ -52,7 +52,8 @@ export class SystemWrapper extends ChainSendContext { } static async load(provider: IProvider): Promise { - let context = new SendContext(provider).logger(new ContractLogger()); + let networkConfig = await provider.getNetworkConfig(); + let context = new SendContext(provider, networkConfig).logger(new ContractLogger()); let builtinFunctions = await ContractWrapper.loadProject(provider, null, SystemConstants.SYSTEM_ABI_PATH, "builtinFunctions", context); let sendWrapper = await ContractWrapper.loadProject(provider, builtinFunctions, SystemConstants.SYSTEM_ABI_PATH, "sendWrapper", context); let esdtSystemContract = await ContractWrapper.loadProject(provider, builtinFunctions, SystemConstants.SYSTEM_ABI_PATH, "esdtSystemContract", context); diff --git a/src/transaction.local.net.spec.ts b/src/transaction.local.net.spec.ts index 9cb834568..673234136 100644 --- a/src/transaction.local.net.spec.ts +++ b/src/transaction.local.net.spec.ts @@ -1,7 +1,6 @@ import { Transaction } from "./transaction"; import { GasLimit } from "./networkParams"; import { TransactionPayload } from "./transactionPayload"; -import { NetworkConfig } from "./networkConfig"; import { Balance } from "./balance"; import { loadTestWallets, TestWallet } from "./testutils"; import { Logger } from "./logger"; @@ -11,6 +10,7 @@ import { TransactionWatcher } from "./transactionWatcher"; describe("test transaction", function () { let alice: TestWallet, bob: TestWallet; + before(async function () { ({ alice, bob } = await loadTestWallets()); }); @@ -20,8 +20,8 @@ describe("test transaction", function () { let provider = chooseProxyProvider("local-testnet"); let watcher = new TransactionWatcher(provider); - - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); + await alice.sync(provider); await bob.sync(provider); @@ -29,12 +29,16 @@ describe("test transaction", function () { let transactionOne = new Transaction({ receiver: bob.address, - value: Balance.egld(42) + value: Balance.egld(42), + gasLimit: network.MinGasLimit, + chainID: network.ChainID }); let transactionTwo = new Transaction({ receiver: bob.address, - value: Balance.egld(43) + value: Balance.egld(43), + gasLimit: network.MinGasLimit, + chainID: network.ChainID }); transactionOne.setNonce(alice.account.nonce); @@ -60,22 +64,23 @@ describe("test transaction", function () { this.timeout(20000); let provider = chooseProxyProvider("local-testnet"); - - await NetworkConfig.getDefault().sync(provider); + let network = await provider.getNetworkConfig(); await alice.sync(provider); let transactionOne = new Transaction({ data: new TransactionPayload("helloWorld"), gasLimit: new GasLimit(70000), receiver: alice.address, - value: Balance.egld(1000) + value: Balance.egld(1000), + chainID: network.ChainID }); let transactionTwo = new Transaction({ data: new TransactionPayload("helloWorld"), gasLimit: new GasLimit(70000), receiver: alice.address, - value: Balance.egld(1000000) + value: Balance.egld(1000000), + chainID: network.ChainID }); transactionOne.setNonce(alice.account.nonce); diff --git a/src/transaction.spec.ts b/src/transaction.spec.ts index 3307ba434..3c1e48bab 100644 --- a/src/transaction.spec.ts +++ b/src/transaction.spec.ts @@ -10,6 +10,9 @@ import { NetworkConfig } from "./networkConfig"; describe("test transaction construction", async () => { let wallets: Record; + let minGasLimit = new GasLimit(50000); + let minGasPrice = new GasPrice(1000000000); + before(async function () { wallets = await loadTestWallets(); }); @@ -19,8 +22,8 @@ describe("test transaction construction", async () => { nonce: new Nonce(89), value: Balance.Zero(), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), - gasLimit: GasLimit.min(), + gasPrice: minGasPrice, + gasLimit: minGasLimit, chainID: new ChainID("local-testnet") }); @@ -34,7 +37,7 @@ describe("test transaction construction", async () => { nonce: new Nonce(90), value: Balance.Zero(), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), + gasPrice: minGasPrice, gasLimit: new GasLimit(80000), data: new TransactionPayload("hello"), chainID: new ChainID("local-testnet") @@ -50,8 +53,8 @@ describe("test transaction construction", async () => { nonce: new Nonce(89), value: Balance.Zero(), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), - gasLimit: GasLimit.min(), + gasPrice: minGasPrice, + gasLimit: minGasLimit, chainID: new ChainID("local-testnet"), version: new TransactionVersion(1), options: new TransactionOptions(1) @@ -67,7 +70,7 @@ describe("test transaction construction", async () => { nonce: new Nonce(91), value: Balance.egld(10), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), + gasPrice: minGasPrice, gasLimit: new GasLimit(100000), data: new TransactionPayload("for the book"), chainID: new ChainID("local-testnet") @@ -83,7 +86,7 @@ describe("test transaction construction", async () => { nonce: new Nonce(92), value: Balance.fromString("123456789000000000000000000000"), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), + gasPrice: minGasPrice, gasLimit: new GasLimit(100000), data: new TransactionPayload("for the spaceship"), chainID: new ChainID("local-testnet") @@ -99,7 +102,7 @@ describe("test transaction construction", async () => { nonce: new Nonce(0), value: Balance.fromString("0"), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), + gasPrice: minGasPrice, gasLimit: new GasLimit(80000), data: new TransactionPayload("hello"), chainID: new ChainID("local-testnet"), @@ -116,8 +119,8 @@ describe("test transaction construction", async () => { nonce: new Nonce(89), value: Balance.Zero(), receiver: wallets.bob.address, - gasPrice: GasPrice.min(), - gasLimit: GasLimit.min(), + gasPrice: minGasPrice, + gasLimit: minGasLimit, chainID: new ChainID("local-testnet") }); diff --git a/src/transaction.ts b/src/transaction.ts index 1d156f610..85e5eb3fe 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -16,7 +16,6 @@ import { guardEmpty, guardNotEmpty } from "./utils"; import { TransactionPayload } from "./transactionPayload"; import * as errors from "./errors"; import { TypedEvent } from "./events"; -import { TransactionWatcher } from "./transactionWatcher"; import { ProtoSerializer } from "./proto"; import { TransactionOnNetwork } from "./transactionOnNetwork"; import { Hash } from "./hash"; @@ -73,7 +72,7 @@ export class Transaction implements ISignable { /** * The chain ID of the Network (e.g. "1" for Mainnet). */ - private readonly chainID: ChainID; + private chainID: ChainID; /** * The version, required by the Network in order to correctly interpret the contents of the transaction. @@ -120,9 +119,9 @@ export class Transaction implements ISignable { receiver: Address; sender?: Address; gasPrice?: GasPrice; - gasLimit?: GasLimit; + gasLimit: GasLimit; data?: TransactionPayload; - chainID?: ChainID; + chainID: ChainID; version?: TransactionVersion; options?: TransactionOptions; }) { @@ -130,10 +129,10 @@ export class Transaction implements ISignable { this.value = value || Balance.Zero(); this.sender = sender || Address.Zero(); this.receiver = receiver; - this.gasPrice = gasPrice || NetworkConfig.getDefault().MinGasPrice; - this.gasLimit = gasLimit || NetworkConfig.getDefault().MinGasLimit; + this.gasPrice = gasPrice || GasPrice.min(); + this.gasLimit = gasLimit; this.data = data || new TransactionPayload(); - this.chainID = chainID || NetworkConfig.getDefault().ChainID; + this.chainID = chainID; this.version = version || TransactionVersion.withDefaultVersion(); this.options = options || TransactionOptions.withDefaultOptions(); @@ -165,7 +164,6 @@ export class Transaction implements ISignable { */ setNonce(nonce: Nonce) { this.nonce = nonce; - this.doAfterPropertySetter(); } getValue(): Balance { @@ -174,7 +172,6 @@ export class Transaction implements ISignable { setValue(value: Balance) { this.value = value; - this.doAfterPropertySetter(); } getSender(): Address { @@ -191,7 +188,6 @@ export class Transaction implements ISignable { setGasPrice(gasPrice: GasPrice) { this.gasPrice = gasPrice; - this.doAfterPropertySetter(); } getGasLimit(): GasLimit { @@ -200,7 +196,6 @@ export class Transaction implements ISignable { setGasLimit(gasLimit: GasLimit) { this.gasLimit = gasLimit; - this.doAfterPropertySetter(); } getData(): TransactionPayload { @@ -211,6 +206,10 @@ export class Transaction implements ISignable { return this.chainID; } + setChainID(chainID: ChainID) { + this.chainID = chainID; + } + getVersion(): TransactionVersion { return this.version; } @@ -219,11 +218,6 @@ export class Transaction implements ISignable { return this.options; } - doAfterPropertySetter() { - this.signature = Signature.empty(); - this.hash = TransactionHash.empty(); - } - getSignature(): Signature { guardNotEmpty(this.signature, "signature"); return this.signature; @@ -311,9 +305,6 @@ export class Transaction implements ISignable { applySignature(signature: ISignatureOfExternalSigner, signedBy: IAddressOfExternalSigner) { let adaptedSignature = adaptToSignature(signature); let adaptedSignedBy = adaptToAddress(signedBy); - - guardEmpty(this.signature, "signature"); - guardEmpty(this.hash, "hash"); this.signature = adaptedSignature; this.sender = adaptedSignedBy;