diff --git a/packages/web3-core/CHANGELOG.md b/packages/web3-core/CHANGELOG.md index 67ef64ece5b..8f61b018479 100644 --- a/packages/web3-core/CHANGELOG.md +++ b/packages/web3-core/CHANGELOG.md @@ -227,4 +227,8 @@ Documentation: - Now when existing packages are added in web3, will be avalible for plugins via context. (#7088) -## [Unreleased] \ No newline at end of file +## [Unreleased] + +### Fixed + +- `setConfig()` fix for `setMaxListenerWarningThreshold` fix (#5079) diff --git a/packages/web3-core/src/web3_config.ts b/packages/web3-core/src/web3_config.ts index 3c335292c4a..03e478e9ecd 100644 --- a/packages/web3-core/src/web3_config.ts +++ b/packages/web3-core/src/web3_config.ts @@ -114,6 +114,14 @@ export abstract class Web3Config const keys = Object.keys(options) as (keyof Web3ConfigOptions)[]; for (const key of keys) { this._triggerConfigChange(key, options[key]); + + if(!isNullish(options[key]) && + typeof options[key] === 'number' && + key === 'maxListenersWarningThreshold' ) + { + // additionally set in event emitter + this.setMaxListenerWarningThreshold(Number(options[key])); + } } Object.assign(this.config, options); } diff --git a/packages/web3-eth-contract/test/integration/setup.js b/packages/web3-eth-contract/test/integration/setup.js index 6170e9ca349..26ba888e0cf 100644 --- a/packages/web3-eth-contract/test/integration/setup.js +++ b/packages/web3-eth-contract/test/integration/setup.js @@ -19,6 +19,6 @@ along with web3.js. If not, see . // eslint-disable-next-line @typescript-eslint/no-require-imports require('../config/setup'); -const jestTimeout = String(process.env.WEB3_SYSTEM_TEST_PROVIDER).includes('ipc') ? 35000 : 15000; +const jestTimeout = String(process.env.WEB3_SYSTEM_TEST_PROVIDER).includes('ipc') ? 35000 : 25000; jest.setTimeout(jestTimeout); diff --git a/packages/web3-eth-contract/test/unit/contract.test.ts b/packages/web3-eth-contract/test/unit/contract.test.ts index 449280c41a7..6ae5f055904 100644 --- a/packages/web3-eth-contract/test/unit/contract.test.ts +++ b/packages/web3-eth-contract/test/unit/contract.test.ts @@ -38,7 +38,6 @@ import { GreeterWithOverloadingBytecode, } from '../shared_fixtures/build/GreeterWithOverloading'; import { AllGetPastEventsData, getLogsData, getPastEventsData } from '../fixtures/unitTestFixtures'; -import { getSystemTestProvider } from '../fixtures/system_test_utils'; import { erc721Abi } from '../fixtures/erc721'; import { ERC20TokenAbi } from '../shared_fixtures/build/ERC20Token'; import { processAsync } from '../shared_fixtures/utils'; @@ -150,7 +149,7 @@ describe('Contract', () => { }); it('should set the provider, from options, upon instantiation', () => { - const provider = getSystemTestProvider(); + const provider = "http://127.0.0.1:4545"; const contract = new Contract([], '', { provider, }); @@ -162,7 +161,7 @@ describe('Contract', () => { }); it('should set the provider, from context, upon instantiation', () => { - const provider = getSystemTestProvider(); + const provider = "http://127.0.0.1:4545"; const contract = new Contract( [], '', @@ -816,9 +815,9 @@ describe('Contract', () => { '0x00000000219ab540356cBB839Cbe05303d7705Fa', { gas: '0x97254' }, ); - + contract.maxListenersWarningThreshold = 1000; + const clonnedContract = contract.clone(); - expect(stringify(contract)).toStrictEqual(stringify(clonnedContract)); contract.options.jsonInterface = GreeterAbi; @@ -826,7 +825,8 @@ describe('Contract', () => { it('should clone new contract', () => { const contract = new Contract(sampleStorageContractABI); - + contract.maxListenersWarningThreshold = 1000; + const clonnedContract = contract.clone(); expect(stringify(contract)).toStrictEqual(stringify(clonnedContract)); }); diff --git a/packages/web3-eth/package.json b/packages/web3-eth/package.json index 75b35059356..3f18fcd83d5 100644 --- a/packages/web3-eth/package.json +++ b/packages/web3-eth/package.json @@ -40,7 +40,7 @@ "test:e2e:sepolia": "jest --config=./test/e2e/jest.config.js --forceExit", "test:watch": "npm test -- --watch", "test:unit": "jest --config=./test/unit/jest.config.js", - "test:integration": "jest --config=./test/integration/jest.config.js --runInBand --forceExit", + "test:integration": "jest --config=./test/integration/jest.config.js --runInBand", "test:coverage:integration": "jest --config=./test/integration/jest.config.js --runInBand --forceExit --coverage=true --coverage-reporters=text", "test:e2e:electron": "npx cypress run --headless --browser electron", "test:e2e:chrome": "npx cypress run --headless --browser chrome", diff --git a/packages/web3-eth/test/integration/defaults.test.ts b/packages/web3-eth/test/integration/defaults.test.ts index a9651fa20e3..351152158b4 100644 --- a/packages/web3-eth/test/integration/defaults.test.ts +++ b/packages/web3-eth/test/integration/defaults.test.ts @@ -41,6 +41,7 @@ import { getSystemTestProvider, isIpc, sendFewSampleTxs, + waitForCondition } from '../fixtures/system_test_utils'; import { @@ -51,11 +52,10 @@ import { import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic'; import { MsgSenderAbi, MsgSenderBytecode } from '../shared_fixtures/build/MsgSender'; import { getTransactionGasPricing } from '../../src/utils/get_transaction_gas_pricing'; -import { Resolve, sendFewTxes } from './helper'; +import { sendFewTxes } from './helper'; describe('defaults', () => { let web3Eth: Web3Eth; - let eth2: Web3Eth; let clientUrl: string | SupportedProviders; let contract: Contract; let deployOptions: Record; @@ -76,10 +76,8 @@ describe('defaults', () => { afterEach(async () => { await closeOpenConnection(web3Eth); - await closeOpenConnection(eth2); + await closeOpenConnection(contract); }); - - describe('defaults', () => { it('defaultAccount', async () => { const tempAcc2 = await createTempAccount(); const tempAcc3 = await createTempAccount(); @@ -102,7 +100,7 @@ describe('defaults', () => { expect(web3Eth.defaultAccount).toBe(tempAcc.address); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { defaultAccount: tempAcc3.address, }, @@ -152,6 +150,8 @@ describe('defaults', () => { expect((fromPass2 as unknown as string).toLowerCase()).toBe( tempAcc2.address.toLowerCase(), ); + await closeOpenConnection(eth2); + await closeOpenConnection(contractMsgFrom); }); it('handleRevert', () => { /* @@ -174,7 +174,7 @@ describe('defaults', () => { expect(web3Eth.handleRevert).toBe(true); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { handleRevert: true, }, @@ -203,7 +203,7 @@ describe('defaults', () => { expect(web3Eth.defaultBlock).toBe('earliest'); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { defaultBlock: 'earliest', @@ -266,8 +266,10 @@ describe('defaults', () => { expect(Number(hexToNumber(storageLatest))).toBe(10); expect(transactionCountLatest).toBe(BigInt(1)); expect(Number(balanceLatest)).toBeGreaterThan(0); + await closeOpenConnection(eth2); + await closeOpenConnection(contractDeployed); }); - it('transactionSendTimeout', () => { + it('transactionSendTimeout', async () => { // default expect(web3Eth.transactionSendTimeout).toBe(750 * 1000); @@ -278,13 +280,14 @@ describe('defaults', () => { expect(web3Eth.transactionSendTimeout).toBe(1); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { transactionSendTimeout: 120, }, }); expect(eth2.transactionSendTimeout).toBe(120); + await closeOpenConnection(eth2); }); it('transactionBlockTimeout', () => { // default @@ -297,14 +300,14 @@ describe('defaults', () => { expect(web3Eth.transactionBlockTimeout).toBe(1); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionBlockTimeout: 120, }, }); expect(eth2.transactionBlockTimeout).toBe(120); }); - it('transactionConfirmationBlocks', () => { + it('transactionConfirmationBlocks default change should work', async () => { // default // eslint-disable-next-line jest/no-standalone-expect expect(web3Eth.transactionConfirmationBlocks).toBe(24); @@ -317,23 +320,28 @@ describe('defaults', () => { expect(web3Eth.transactionConfirmationBlocks).toBe(3); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionConfirmationBlocks: 4, }, }); // eslint-disable-next-line jest/no-standalone-expect expect(eth2.transactionConfirmationBlocks).toBe(4); + }); + it('transactionConfirmationBlocks implementation', async () => { const tempAcc2 = await createTempAccount(); const waitConfirmations = 1; - const eth = new Web3Eth(web3Eth.provider); + const eth = new Web3Eth(getSystemTestProvider()); eth.setConfig({ transactionConfirmationBlocks: waitConfirmations }); - + const from = tempAcc.address; const to = tempAcc2.address; const value = `0x1`; + + let confirmationCount = 0; + const sentTx: Web3PromiEvent< TransactionReceipt, SendTransactionEvents @@ -341,35 +349,29 @@ describe('defaults', () => { to, value, from, + }).on('confirmation', (_data) => { + confirmationCount += 1; + if (confirmationCount >= waitConfirmations) { + sentTx.removeAllListeners(); // Clean up listeners + } }); - const receiptPromise = new Promise((resolve: Resolve) => { - // Tx promise is handled separately - // eslint-disable-next-line no-void - void sentTx.on('receipt', (params: TransactionReceipt) => { - expect(Number(params.status)).toBe(1); - resolve(); - }); - }); - let shouldBe = 1; - const confirmationPromise = new Promise((resolve: Resolve) => { - // Tx promise is handled separately - // eslint-disable-next-line no-void - void sentTx.on('confirmation', ({ confirmations }) => { - expect(Number(confirmations)).toBeGreaterThanOrEqual(shouldBe); - shouldBe += 1; - if (shouldBe > waitConfirmations) { - resolve(); - } - }); - }); - await sentTx; - await receiptPromise; - await sendFewSampleTxs(isIpc ? 2 * waitConfirmations : waitConfirmations); - await confirmationPromise; - await closeOpenConnection(eth); + const receipt = await sentTx; + expect(Number(receipt.status)).toBe(1); + + // Optionally send a few sample transactions + await sendFewSampleTxs(isIpc ? 5 * waitConfirmations : 2 * waitConfirmations); + + expect(confirmationCount).toBe(waitConfirmations); + + await waitForCondition( + () => confirmationCount >= waitConfirmations, + async () => { await closeOpenConnection(eth)}, + 10, + 5000); }); - it('transactionPollingInterval and transactionPollingTimeout', () => { + + it('transactionPollingInterval and transactionPollingTimeout', async () => { // default expect(web3Eth.transactionPollingInterval).toBe(1000); expect(web3Eth.transactionPollingTimeout).toBe(750 * 1000); @@ -383,7 +385,7 @@ describe('defaults', () => { expect(web3Eth.transactionPollingTimeout).toBe(10); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionPollingInterval: 400, transactionPollingTimeout: 10, @@ -451,7 +453,7 @@ describe('defaults', () => { expect(web3Eth.transactionConfirmationPollingInterval).toBe(10); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionReceiptPollingInterval: 400, transactionConfirmationPollingInterval: 10, @@ -471,7 +473,7 @@ describe('defaults', () => { expect(web3Eth.blockHeaderTimeout).toBe(3); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { blockHeaderTimeout: 4, }, @@ -497,7 +499,7 @@ describe('defaults', () => { ); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { enableExperimentalFeatures: { useSubscriptionWhenCheckingBlockTimeout: true, @@ -524,7 +526,7 @@ describe('defaults', () => { expect(web3Eth.enableExperimentalFeatures.useRpcCallSpecification).toBe(true); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { enableExperimentalFeatures: { useSubscriptionWhenCheckingBlockTimeout: false, @@ -533,6 +535,7 @@ describe('defaults', () => { }, }); expect(eth2.enableExperimentalFeatures.useRpcCallSpecification).toBe(true); + }); it('should fallback to polling if provider support `on` but `newBlockHeaders` does not arrive in `blockHeaderTimeout` seconds', async () => { @@ -572,6 +575,7 @@ describe('defaults', () => { value, }); + let confirmationCount = 0; const confirmationPromise = new Promise((resolve: (status: bigint) => void) => { // Tx promise is handled separately // eslint-disable-next-line no-void @@ -584,45 +588,47 @@ describe('defaults', () => { confirmations: bigint; receipt: { status: bigint }; }) => { + confirmationCount = Number(confirmations); // Being able to get 2 confirmations means the polling for new blocks works if (confirmations >= 2) { sentTx.removeAllListeners(); resolve(status); } else { - // Send a transaction to cause dev providers creating new blocks to fire the 'confirmation' event again. - await tempEth.sendTransaction({ - from, - to, - value, - }); + // Send few transaction to cause dev providers creating new blocks to fire the 'confirmation' event again. + await sendFewSampleTxs(5); } }, ); }); - await sentTx; + const receipt = await sentTx; + expect(Number(receipt.status)).toBe(1); // Ensure the promise the get the confirmations resolves with no error const status = await confirmationPromise; expect(status).toBe(BigInt(1)); - await closeOpenConnection(tempEth); + + await waitForCondition( + () => confirmationCount >= 2, + async () => { await closeOpenConnection(tempEth)}); }); + it('maxListenersWarningThreshold test default config', () => { // default expect(web3Eth.maxListenersWarningThreshold).toBe(100); }); it('maxListenersWarningThreshold set maxListeners through variable', () => { - eth2 = new Web3Eth({}); + const eth2 = new Web3Eth({}); eth2.maxListenersWarningThreshold = 3; expect(eth2.maxListenersWarningThreshold).toBe(3); expect(eth2.getMaxListeners()).toBe(3); }); it('maxListenersWarningThreshold set config', () => { - const eth = new Web3Eth({}); - eth.setConfig({ + const eth3 = new Web3Eth({}); + eth3.setConfig({ maxListenersWarningThreshold: 3, }); - expect(eth2.maxListenersWarningThreshold).toBe(3); - expect(eth2.getMaxListeners()).toBe(3); + expect(eth3.maxListenersWarningThreshold).toBe(3); + expect(eth3.getMaxListeners()).toBe(3); }); it('defaultNetworkId', async () => { // default @@ -635,7 +641,7 @@ describe('defaults', () => { expect(web3Eth.defaultNetworkId).toBe(3); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { defaultNetworkId: 4, @@ -666,6 +672,8 @@ describe('defaults', () => { }); expect(resWithPassNetworkId.networkId).toBe(BigInt(5)); + + await closeOpenConnection(eth2); }); it('defaultChain', async () => { // default @@ -678,7 +686,7 @@ describe('defaults', () => { expect(web3Eth.defaultChain).toBe('ropsten'); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { defaultChain: 'rinkeby', @@ -695,6 +703,7 @@ describe('defaults', () => { web3Context: eth2 as Web3Context, }); expect(res.chain).toBe('rinkeby'); + await closeOpenConnection(eth2); }); it('defaultHardfork', async () => { // default @@ -707,7 +716,7 @@ describe('defaults', () => { expect(web3Eth.defaultHardfork).toBe('dao'); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { defaultHardfork: 'istanbul', @@ -732,6 +741,7 @@ describe('defaults', () => { eth2, ); expect(res.common.hardfork()).toBe('istanbul'); + await closeOpenConnection(eth2); }); it('defaultCommon', () => { // default @@ -754,13 +764,14 @@ describe('defaults', () => { expect(web3Eth.defaultCommon).toBe(common); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { defaultCommon: common, }, }); expect(eth2.defaultCommon).toBe(common); }); + it('defaultTransactionType', async () => { // default expect(web3Eth.defaultTransactionType).toBe('0x2'); @@ -770,15 +781,20 @@ describe('defaults', () => { }); expect(web3Eth.defaultTransactionType).toBe('0x3'); + // revert back to default + web3Eth.setConfig({ + defaultTransactionType: '0x2', + }); + // set by create new instance - eth2 = new Web3Eth({ - provider: clientUrl, + const eth = new Web3Eth({ + provider: web3Eth.provider, config: { defaultTransactionType: '0x4444', }, }); - expect(eth2.defaultTransactionType).toBe('0x4444'); + expect(eth.defaultTransactionType).toBe('0x4444'); const res = getTransactionType( { @@ -791,7 +807,7 @@ describe('defaults', () => { chainId: '0x1', gasLimit: '0x5208', }, - eth2, + eth, ); expect(res).toBe('0x4444'); @@ -812,7 +828,7 @@ describe('defaults', () => { gasLimit: '0x5208', maxFeePerGas: '0x32', }, - eth2, + eth, ); expect(maxFeePerGasOverride).toBe('0x2'); const maxPriorityFeePerGasOverride = getTransactionType( @@ -827,7 +843,7 @@ describe('defaults', () => { gasLimit: '0x5208', maxPriorityFeePerGas: '0x32', }, - eth2, + eth, ); expect(maxPriorityFeePerGasOverride).toBe('0x2'); const hardforkOverride = getTransactionType( @@ -842,7 +858,7 @@ describe('defaults', () => { gasLimit: '0x5208', hardfork: 'london', }, - eth2, + eth, ); expect(hardforkOverride).toBe('0x2'); const commonOverride = getTransactionType( @@ -860,7 +876,7 @@ describe('defaults', () => { hardfork: 'london', }, }, - eth2, + eth, ); expect(commonOverride).toBe('0x2'); @@ -884,7 +900,7 @@ describe('defaults', () => { }, ], }, - eth2, + eth, ); expect(accessListOverride).toBe('0x1'); @@ -900,7 +916,7 @@ describe('defaults', () => { gasLimit: '0x5208', hardfork: 'berlin', }, - eth2, + eth, ); expect(hardforkBerlinOverride).toBe('0x0'); @@ -919,9 +935,10 @@ describe('defaults', () => { hardfork: 'berlin', }, }, - eth2, + eth, ); expect(commonBerlinOverride).toBe('0x0'); + await closeOpenConnection(eth); }); it('defaultMaxPriorityFeePerGas', async () => { // default @@ -933,7 +950,7 @@ describe('defaults', () => { expect(web3Eth.defaultMaxPriorityFeePerGas).toBe(numberToHex(2100000000)); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ provider: web3Eth.provider, config: { defaultMaxPriorityFeePerGas: numberToHex(1200000000), @@ -976,6 +993,7 @@ describe('defaults', () => { DEFAULT_RETURN_FORMAT, ); expect(resOverride?.maxPriorityFeePerGas).toBe(BigInt('4883362083')); + await closeOpenConnection(eth2); }); it('transactionBuilder', async () => { // default @@ -992,7 +1010,7 @@ describe('defaults', () => { expect(web3Eth.transactionBuilder).toBe(newBuilderMock); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionBuilder: newBuilderMock, }, @@ -1027,7 +1045,7 @@ describe('defaults', () => { expect(web3Eth.transactionTypeParser).toBe(newParserMock); // set by create new instance - eth2 = new Web3Eth({ + const eth2 = new Web3Eth({ config: { transactionTypeParser: newParserMock, }, @@ -1049,5 +1067,4 @@ describe('defaults', () => { ); expect(newParserMock).toHaveBeenCalled(); }); - }); -}); +}); \ No newline at end of file diff --git a/packages/web3-eth/test/integration/defaults.transactionBlockTimeout.test.ts b/packages/web3-eth/test/integration/defaults.transactionBlockTimeout.test.ts index fe679bb0dbe..ba7fd5f15e0 100644 --- a/packages/web3-eth/test/integration/defaults.transactionBlockTimeout.test.ts +++ b/packages/web3-eth/test/integration/defaults.transactionBlockTimeout.test.ts @@ -28,12 +28,12 @@ import { getSystemTestProvider, isSocket, itIf, - waitForOpenConnection, createLocalAccount, sendFewSampleTxs, getSystemTestBackend, describeIf, BACKEND + } from '../fixtures/system_test_utils'; const MAX_32_SIGNED_INTEGER = 2147483647; @@ -45,28 +45,72 @@ describeIf(getSystemTestBackend() !== BACKEND.HARDHAT)('defaults', () => { let clientUrl: string | SupportedProviders; let account1: Web3Account; let account2: Web3Account; + let transactionBlockTimeout: number; + let transactionSendTimeout: number; + let transactionPollingTimeout: number; + let blockHeaderTimeout: number; beforeEach(() => { clientUrl = getSystemTestProvider(); web3 = new Web3(clientUrl); + transactionBlockTimeout = web3.eth.transactionBlockTimeout; + transactionSendTimeout = web3.eth.transactionSendTimeout; + transactionPollingTimeout = web3.eth.transactionPollingTimeout; + blockHeaderTimeout = web3.eth.blockHeaderTimeout; + }); + + afterEach(async () => { + web3.eth.transactionBlockTimeout = transactionBlockTimeout; + web3.eth.transactionSendTimeout = transactionSendTimeout ; + web3.eth.transactionPollingTimeout = transactionPollingTimeout; + web3.eth.blockHeaderTimeout = blockHeaderTimeout; + await closeOpenConnection(web3); + }); + + test('should fail if transaction was not mined within `transactionBlockTimeout` blocks', async () => { + account1 = await createLocalAccount(web3); + account2 = await createLocalAccount(web3); + + const sentTx: Web3PromiEvent< + TransactionReceipt, + SendTransactionEvents + > = web3.eth.sendTransaction({ + from: account1.address, + to: account2.address, + gas, + value: '0x1', + // Give a high nonce so the transaction stuck forever. + // However, make this random to be able to run the test many times without receiving an error that indicate submitting the same transaction twice. + nonce: Number.MAX_SAFE_INTEGER, + }); + + // Some providers (mostly used for development) will make blocks only when there are new transactions + // So, send 2 transactions, one after another, because in this test `transactionBlockTimeout = 2`. + // eslint-disable-next-line no-void + await sendFewSampleTxs(2); + // Increase other timeouts so only `transactionBlockTimeout` would be reached web3.eth.transactionSendTimeout = MAX_32_SIGNED_INTEGER; web3.eth.transactionPollingTimeout = MAX_32_SIGNED_INTEGER; web3.eth.blockHeaderTimeout = MAX_32_SIGNED_INTEGER / 1000; - }); + web3.eth.transactionBlockTimeout = 2; + + await expect(sentTx).rejects.toThrow(/was not mined within [0-9]+ blocks/); + + await expect(sentTx).rejects.toThrow(TransactionBlockTimeoutError); - afterEach(async () => { - web3.eth.transactionBlockTimeout = 50; - await closeOpenConnection(web3.eth); }); - describe('defaults', () => { - it('should fail if transaction was not mined within `transactionBlockTimeout` blocks', async () => { + // The code of this test case is identical to the pervious one except for `eth.enableExperimentalFeatures = true` + // TODO: And this test case will be removed once https://github.com/web3/web3.js/issues/5521 is implemented. + itIf(isSocket)( + 'should fail if transaction was not mined within `transactionBlockTimeout` blocks - when subscription is used', + async () => { account1 = await createLocalAccount(web3); account2 = await createLocalAccount(web3); - // Setting a high `nonce` when sending a transaction, to cause the RPC call to stuck at the Node + // Setting a high `nonce` when sending a transaction, to cause the RPC call to stuck at the Node const sentTx: Web3PromiEvent< TransactionReceipt, SendTransactionEvents @@ -75,6 +119,7 @@ describeIf(getSystemTestBackend() !== BACKEND.HARDHAT)('defaults', () => { to: account2.address, gas, value: '0x1', + type: '0x1', // Give a high nonce so the transaction stuck forever. // However, make this random to be able to run the test many times without receiving an error that indicate submitting the same transaction twice. nonce: Number.MAX_SAFE_INTEGER, @@ -82,58 +127,19 @@ describeIf(getSystemTestBackend() !== BACKEND.HARDHAT)('defaults', () => { // Some providers (mostly used for development) will make blocks only when there are new transactions // So, send 2 transactions, one after another, because in this test `transactionBlockTimeout = 2`. - // eslint-disable-next-line no-void + // eslint-disable-next-line no-void, @typescript-eslint/no-unsafe-call await sendFewSampleTxs(2); - + web3.eth.transactionBlockTimeout = 2; + // using subscription to get the new blocks and fire `TransactionBlockTimeoutError` is currently supported only + // with `enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout` equal true. + web3.eth.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout = true; + await expect(sentTx).rejects.toThrow(/was not mined within [0-9]+ blocks/); await expect(sentTx).rejects.toThrow(TransactionBlockTimeoutError); - await closeOpenConnection(web3.eth); - }); - - // The code of this test case is identical to the pervious one except for `eth.enableExperimentalFeatures = true` - // TODO: And this test case will be removed once https://github.com/web3/web3.js/issues/5521 is implemented. - itIf(isSocket)( - 'should fail if transaction was not mined within `transactionBlockTimeout` blocks - when subscription is used', - async () => { - account1 = await createLocalAccount(web3); - account2 = await createLocalAccount(web3); - await waitForOpenConnection(web3.eth); - // using subscription to get the new blocks and fire `TransactionBlockTimeoutError` is currently supported only - // with `enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout` equal true. - web3.eth.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout = true; - - // Setting a high `nonce` when sending a transaction, to cause the RPC call to stuck at the Node - const sentTx: Web3PromiEvent< - TransactionReceipt, - SendTransactionEvents - > = web3.eth.sendTransaction({ - from: account1.address, - to: account2.address, - gas, - value: '0x1', - type: '0x1', - // Give a high nonce so the transaction stuck forever. - // However, make this random to be able to run the test many times without receiving an error that indicate submitting the same transaction twice. - nonce: Number.MAX_SAFE_INTEGER, - }); - - // Some providers (mostly used for development) will make blocks only when there are new transactions - // So, send 2 transactions, one after another, because in this test `transactionBlockTimeout = 2`. - // eslint-disable-next-line no-void, @typescript-eslint/no-unsafe-call - void sendFewSampleTxs(2); - - web3.eth.transactionBlockTimeout = 2; - - await expect(sentTx).rejects.toThrow(/was not mined within [0-9]+ blocks/); - - await expect(sentTx).rejects.toThrow(TransactionBlockTimeoutError); - - await closeOpenConnection(web3.eth); - }, - ); - }); + }, + ); }); diff --git a/packages/web3-eth/test/integration/get_revert_reason.test.ts b/packages/web3-eth/test/integration/get_revert_reason.test.ts index 5318b06400c..4180742a21b 100644 --- a/packages/web3-eth/test/integration/get_revert_reason.test.ts +++ b/packages/web3-eth/test/integration/get_revert_reason.test.ts @@ -26,7 +26,8 @@ import { createTempAccount, getSystemTestBackend, getSystemTestProvider, - BACKEND + BACKEND, + closeOpenConnection } from '../fixtures/system_test_utils'; describe('Web3Eth.getRevertReason', () => { @@ -49,6 +50,10 @@ describe('Web3Eth.getRevertReason', () => { .contractAddress as Address; }); + afterAll(async () => { + await closeOpenConnection(web3Eth); + }); + it('should return reason for a contract call', async () => { const transaction: TransactionCall = { from: tempAccount.address, diff --git a/packages/web3-eth/test/integration/watch_transaction.test.ts b/packages/web3-eth/test/integration/watch_transaction.test.ts index 4aa607c8fdd..733b64a089b 100644 --- a/packages/web3-eth/test/integration/watch_transaction.test.ts +++ b/packages/web3-eth/test/integration/watch_transaction.test.ts @@ -25,11 +25,11 @@ import { describeIf, closeOpenConnection, isSocket, - waitForOpenConnection, createLocalAccount, isIpc, sendFewSampleTxs, createAccount, + waitForCondition, } from '../fixtures/system_test_utils'; const waitConfirmations = 2; @@ -46,7 +46,6 @@ describeIf(isSocket)('watch subscription transaction', () => { web3 = new Web3(clientUrl); account1 = await createLocalAccount(web3); account2 = createAccount(); - await waitForOpenConnection(web3.eth); }); describe('wait for confirmation subscription', () => { it('subscription to heads', async () => { @@ -83,10 +82,18 @@ describeIf(isSocket)('watch subscription transaction', () => { } }); }); + await receiptPromise; await sendFewSampleTxs(isIpc ? 2 * waitConfirmations : waitConfirmations); - await confirmationPromise; - await closeOpenConnection(web3.eth); + + const resourcePromise = waitForCondition( + () => shouldBe >= waitConfirmations, + async () => { + sentTx.removeAllListeners(); + await closeOpenConnection(web3);} + ); + + await Promise.all([confirmationPromise,resourcePromise]); }); }); }); diff --git a/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts b/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts index 9b0eda360dc..b16479d886b 100644 --- a/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts +++ b/packages/web3-eth/test/integration/web3_eth/send_transaction.test.ts @@ -99,6 +99,8 @@ describe('Web3Eth.sendTransaction', () => { to: '0x0000000000000000000000000000000000000000', value: BigInt(1), }); + + await closeOpenConnection(web3EthWithWallet); }); it('should make a simple value transfer - with local wallet indexed receiver', async () => { @@ -130,6 +132,8 @@ describe('Web3Eth.sendTransaction', () => { to: wallet.get(0)?.address.toLowerCase(), value: BigInt(1), }); + + await closeOpenConnection(web3EthWithWallet); }); it('should make a simple value transfer - with local wallet indexed sender and receiver', async () => { @@ -165,6 +169,8 @@ describe('Web3Eth.sendTransaction', () => { to: wallet.get(1)?.address.toLowerCase(), value: BigInt(1), }); + + await closeOpenConnection(web3EthWithWallet); }); it('should make a transaction with no value transfer', async () => { const transaction: Transaction = { diff --git a/scripts/system_tests_utils.ts b/scripts/system_tests_utils.ts index 48ea2129c06..11f9bcef717 100644 --- a/scripts/system_tests_utils.ts +++ b/scripts/system_tests_utils.ts @@ -50,16 +50,16 @@ import { Web3EthExecutionAPI, FMT_NUMBER, FMT_BYTES, + TransactionReceipt, } from 'web3-types'; // eslint-disable-next-line import/no-extraneous-dependencies import { Personal } from 'web3-eth-personal'; // eslint-disable-next-line import/no-extraneous-dependencies -import Web3 from 'web3'; +import {Web3, WebSocketProvider } from 'web3'; // eslint-disable-next-line import/no-extraneous-dependencies import { NonPayableMethodObject } from 'web3-eth-contract'; -// eslint-disable-next-line import/no-extraneous-dependencies -import HttpProvider from 'web3-providers-http'; + // eslint-disable-next-line import/no-extraneous-dependencies import { IpcProvider } from 'web3-providers-ipc'; import accountsString from './accounts.json'; @@ -83,13 +83,14 @@ export const BACKEND = { MAINNET: 'mainnet', }; -export const getSystemTestProviderUrl = (): string => +export const getSystemTestProviderUrl = (): string => getEnvVar('WEB3_SYSTEM_TEST_PROVIDER') ?? DEFAULT_SYSTEM_PROVIDER; export const getSystemTestProvider = (): | string | SupportedProviders => { const url = getSystemTestProviderUrl(); + if (url.includes('ipc')) { return new IpcProvider(url); } @@ -155,37 +156,21 @@ export const waitForOpenConnection = async ( export const closeOpenConnection = async (web3Context: Web3Context) => { if ( - !isSocket || - web3Context?.provider instanceof HttpProvider || - (web3Context?.provider?.supportsSubscriptions && - !web3Context.provider?.supportsSubscriptions()) - ) { - return; - } - // make sure we try to close the connection after it is established - if ( - web3Context?.provider && - (web3Context.provider as unknown as Web3BaseProvider).getStatus() === 'connecting' - ) { - await waitForOpenConnection(web3Context); - } - // If an error happened during closing, that is acceptable at tests, just print a 'warn'. - if (web3Context?.provider) { - (web3Context.provider as unknown as Web3BaseProvider).on('error', (err: any) => { - console.warn('error while trying to close the connection', err); - }); - } - // Wait a bit to ensure the connection does not have a pending data that - // could cause an error if written after closing the connection. - await new Promise(resolve => { - setTimeout(resolve, 500); - }); - if ( - web3Context?.provider && + web3Context?.provider && ( + web3Context?.provider instanceof WebSocketProvider || + web3Context?.provider instanceof IpcProvider + + ) && 'disconnect' in (web3Context.provider as unknown as Web3BaseProvider) ) { - (web3Context.provider as unknown as Web3BaseProvider).disconnect(1000, ''); - } + + (web3Context.provider as unknown as Web3BaseProvider).reset(); + (web3Context.provider as unknown as Web3BaseProvider).disconnect(); + + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + } }; export const createAccountProvider = (context: Web3Context) => { @@ -241,11 +226,16 @@ export const createAccountProvider = (context: Web3Context) => export const refillAccount = async (from: string, to: string, value: string | number) => { const web3Eth = new Web3Eth(DEFAULT_SYSTEM_PROVIDER); - await web3Eth.sendTransaction({ + const receipt = await web3Eth.sendTransaction({ from, to, value, }); + + if(receipt.status !== BigInt(1)) + throw new Error("refillAccount failed"); + + await closeOpenConnection(web3Eth); }; let mainAcc: string; @@ -256,6 +246,7 @@ export const createNewAccount = async (config?: { password?: string; doNotImport?: boolean; }): Promise<{ address: string; privateKey: string }> => { + const acc = config?.privateKey ? privateKeyToAccount(config?.privateKey) : _createAccount(); const clientUrl = DEFAULT_SYSTEM_PROVIDER; @@ -267,6 +258,7 @@ export const createNewAccount = async (config?: { await web3.hardhat.impersonateAccount(acc.address); // await impersonateAccount(acc.address); await web3.hardhat.setBalance(acc.address, web3.utils.toHex('100000000')); + await closeOpenConnection(web3); } else { const web3Personal = new Personal(clientUrl); if (!config?.doNotImport) { @@ -279,6 +271,7 @@ export const createNewAccount = async (config?: { } await web3Personal.unlockAccount(acc.address, config.password ?? '123456', 100000000); + await closeOpenConnection(web3Personal); } } @@ -288,17 +281,21 @@ export const createNewAccount = async (config?: { const web3 = new Web3(url); web3.registerPlugin(new HardhatPlugin()); await web3.hardhat.setBalance(acc.address, web3.utils.toHex('100000000')); + await closeOpenConnection(web3); } else { const web3Personal = new Personal(clientUrl); if (!mainAcc) { [mainAcc] = await web3Personal.getAccounts(); } await refillAccount(mainAcc, acc.address, '100000000000000000'); + await closeOpenConnection(web3Personal); } } return { address: acc.address.toLowerCase(), privateKey: acc.privateKey }; }; + + let tempAccountList: { address: string; privateKey: string }[] = []; const walletsOnWorker = 20; @@ -485,16 +482,21 @@ export const sendFewSampleTxs = async (cnt = 1) => { const web3 = new Web3(DEFAULT_SYSTEM_PROVIDER); const fromAcc = await createLocalAccount(web3); const toAcc = createAccount(); - const res = []; + const res: TransactionReceipt[]= []; for (let i = 0; i < cnt; i += 1) { + // eslint-disable-next-line no-await-in-loop + const receipt = await web3.eth.sendTransaction({ + to: toAcc.address, + value: '0x1', + from: fromAcc.address, + gas: '300000', + }); + + if(receipt.status !== BigInt(1)) + throw new Error("sendFewSampleTxs failed "); + res.push( - // eslint-disable-next-line no-await-in-loop - await web3.eth.sendTransaction({ - to: toAcc.address, - value: '0x1', - from: fromAcc.address, - gas: '300000', - }), + receipt ); } await closeOpenConnection(web3); @@ -516,3 +518,34 @@ export const mapFormatToType: { [key: string]: string } = { [FMT_BYTES.HEX]: 'string', [FMT_BYTES.UINT8ARRAY]: 'object', }; + +export const waitForCondition = async ( + conditionFunc: () => boolean, + logicFunc: () => Promise | void, + maxIterations = 10, // 10 times + duration = 8000, // check after each 8 seconds +): Promise => { + return new Promise((resolve, reject) => { + let iterations = 0; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + const interval = setInterval(async () => { + try { + if (iterations > 0 && conditionFunc()) { // wait duration before first check + clearInterval(interval); + await logicFunc(); + resolve(); + } else { + iterations += 1; + if (iterations >= maxIterations) { + clearInterval(interval); + await logicFunc(); + reject(new Error('Condition not met after 10 iterations.')); + } + } + } catch (error) { + clearInterval(interval); + reject(error); + } + }, duration); + }); +}; \ No newline at end of file diff --git a/templates/cypress.config.js b/templates/cypress.config.js index 95874902d93..977e51c90db 100644 --- a/templates/cypress.config.js +++ b/templates/cypress.config.js @@ -25,6 +25,7 @@ const config = { }, specPattern: 'test/integration/**/**/*.test.ts', excludeSpecPattern: ['**/contract_defaults_extra.test.ts'], + defaultCommandTimeout: 120000 }, };