diff --git a/packages/web3-eth/src/utils/watch_transaction_by_polling.ts b/packages/web3-eth/src/utils/watch_transaction_by_polling.ts
index 34e01581422..a429eabb818 100644
--- a/packages/web3-eth/src/utils/watch_transaction_by_polling.ts
+++ b/packages/web3-eth/src/utils/watch_transaction_by_polling.ts
@@ -56,8 +56,10 @@ export const watchTransactionByPolling = <
let confirmations = 1;
const intervalId = setInterval(() => {
(async () => {
- if (confirmations >= web3Context.transactionConfirmationBlocks)
+ if (confirmations >= web3Context.transactionConfirmationBlocks){
clearInterval(intervalId);
+ return;
+ }
const nextBlock = await ethRpcMethods.getBlockByNumber(
web3Context.requestManager,
diff --git a/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts b/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts
index d7f4169aeaf..23c92c793a6 100644
--- a/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts
+++ b/packages/web3-eth/test/unit/utils/watch_transaction_by_subscription.test.ts
@@ -14,117 +14,124 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
-import { Web3Context, Web3RequestManager } from 'web3-core';
-import { format } from 'web3-utils';
-import { DEFAULT_RETURN_FORMAT, JsonRpcResponseWithResult, Web3EthExecutionAPI } from 'web3-types';
-import { ethRpcMethods } from 'web3-rpc-methods';
-import { WebSocketProvider } from 'web3-providers-ws';
+
+import { Web3Context } from 'web3-core';
+import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types';
+import WebSocketProvider from 'web3-providers-ws';
+
import * as rpcMethodWrappers from '../../../src/rpc_method_wrappers';
-import * as WatchTransactionBySubscription from '../../../src/utils/watch_transaction_by_subscription';
import {
expectedTransactionReceipt,
expectedTransactionHash,
testData,
} from '../rpc_method_wrappers/fixtures/send_signed_transaction';
-import { transactionReceiptSchema } from '../../../src/schemas';
-import { registeredSubscriptions } from '../../../src';
+import { blockMockResult } from '../../fixtures/transactions_data';
+
-jest.mock('web3-rpc-methods');
jest.mock('web3-providers-ws');
-jest.mock('../../../src/utils/watch_transaction_by_polling');
const testMessage =
'Title: %s\ninputSignedTransaction: %s\nexpectedTransactionHash: %s\nexpectedTransactionReceipt: %s\n';
-async function waitUntilCalled(mock: jest.Mock, timeout = 1000): Promise {
- return new Promise((resolve, reject) => {
- let timeoutId: NodeJS.Timeout | undefined;
- const intervalId = setInterval(() => {
- if (mock.mock.calls.length > 0) {
- clearInterval(intervalId);
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- resolve(mock);
- }
- }, 100);
- timeoutId = setTimeout(() => {
- clearInterval(intervalId);
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- reject(new Error('timeout'));
- }, timeout);
- });
-}
describe('watchTransactionBySubscription', () => {
+ const CONFIRMATION_BLOCKS = 5;
describe('should revert to polling in cases where getting by subscription did not workout', () => {
let web3Context: Web3Context;
beforeEach(() => {
- jest.spyOn(Web3RequestManager.prototype, 'send').mockImplementation(async () => {
- return {} as Promise;
- });
- jest.spyOn(WebSocketProvider.prototype, 'request').mockImplementation(async () => {
- return {} as Promise>;
- });
-
- (ethRpcMethods.sendRawTransaction as jest.Mock).mockResolvedValue(
- expectedTransactionHash,
- );
- (ethRpcMethods.getTransactionReceipt as jest.Mock).mockResolvedValue(
- expectedTransactionHash,
- );
web3Context = new Web3Context({
- // dummy provider that does supports subscription
- provider: new WebSocketProvider('ws://localhost:8546'),
- registeredSubscriptions,
- });
+ provider: new WebSocketProvider('wss://localhost:8546'),}
+ );
+
(web3Context.provider as any).supportsSubscriptions = () => true;
+ web3Context.transactionConfirmationBlocks = CONFIRMATION_BLOCKS;
+ web3Context.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout =
+ true;
+
});
- afterEach(() => {
- // to clear the interval inside the subscription function:
- web3Context.transactionConfirmationBlocks = 0;
- });
- let counter = 0;
- it.each(testData)(
- `should call getBlockNumber if blockHeaderTimeout reached\n ${testMessage}`,
- async (_, inputTransaction) => {
- if (counter > 0) {
- return;
- }
- counter += 1;
- const formattedTransactionReceipt = format(
- transactionReceiptSchema,
- expectedTransactionReceipt,
- DEFAULT_RETURN_FORMAT,
- );
- web3Context.enableExperimentalFeatures.useSubscriptionWhenCheckingBlockTimeout =
- true;
- // this will case the function to revert to polling:
- web3Context.blockHeaderTimeout = 0;
+ it.each(testData)(
+ `should call getBlockByNumber if blockHeaderTimeout reached\n ${testMessage}`,
+ async (_, inputTransaction,) => {
- web3Context.transactionSendTimeout = 2;
+ let blockNum = 100;
+ let ethGetBlockByNumberCount = 0;
+ web3Context.requestManager.send = jest.fn(async (request) => {
+
+ if (request.method === 'eth_getBlockByNumber') {
+ ethGetBlockByNumberCount += 1;
+ return Promise.resolve(
+ { ...blockMockResult.result,
+ number: (request as any).params[0]
+ });
+ }
+ if (request.method === 'eth_call') {
+
+ return Promise.resolve("0x");
+ }
+ if (request.method === 'eth_blockNumber') {
+ blockNum += 1;
+ return Promise.resolve(blockNum.toString(16));
+ }
+ if (request.method === 'eth_sendRawTransaction') {
+ return Promise.resolve(expectedTransactionHash);
+ }
+ if (request.method === 'eth_getTransactionReceipt') {
+ return Promise.resolve(expectedTransactionReceipt);
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return Promise.reject(new Error("Unknown Request")) as any;
+ });
const promiEvent = rpcMethodWrappers.sendSignedTransaction(
web3Context,
inputTransaction,
DEFAULT_RETURN_FORMAT,
);
- // await promiEvent;
- WatchTransactionBySubscription.watchTransactionBySubscription({
- web3Context,
- transactionReceipt: formattedTransactionReceipt,
- transactionPromiEvent: promiEvent,
- returnFormat: DEFAULT_RETURN_FORMAT,
+
+ let confirmationsCount = 0;
+ const confirmationPromise = new Promise((resolve, reject) => {
+
+ const handleConfirmation = (confirmation: { confirmations: bigint }) => {
+ confirmationsCount += 1;
+
+ if (confirmation.confirmations >= CONFIRMATION_BLOCKS) {
+ resolve();
+ }
+ };
+
+ const handleError = (_error: any) => {
+ reject();
+ };
+
+ promiEvent
+ .on('confirmation', handleConfirmation)
+ .on('error', handleError)
+ .then((res) => {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(res).toBeDefined();
+ })
+ .catch(reject);
+ });
+
+ // Wait for the confirmationPromise to resolve or timeout after 5 seconds
+ let timeoutId;
+ const timeout = new Promise((_res, reject) => {
+ timeoutId = setTimeout(() => reject(new Error('Timeout waiting for confirmations')), 500000);
});
- await waitUntilCalled(ethRpcMethods.getBlockNumber as jest.Mock, 5000);
- await promiEvent;
- },
- 60000,
+ await Promise.race([confirmationPromise, timeout]);
+
+ clearTimeout(timeoutId);
+
+ expect(confirmationsCount).toBe(CONFIRMATION_BLOCKS);
+ expect(ethGetBlockByNumberCount).toBe(CONFIRMATION_BLOCKS - 1); // means polling called getblock 4 times as first confirmation is receipt it self
+
+ }
);
+
+
});
});