diff --git a/packages/web3-core/src/web3_request_manager.ts b/packages/web3-core/src/web3_request_manager.ts index 84f4db068f8..1cbe9377988 100644 --- a/packages/web3-core/src/web3_request_manager.ts +++ b/packages/web3-core/src/web3_request_manager.ts @@ -210,7 +210,7 @@ export class Web3RequestManager< ) as JsonRpcPayload; if (!isNullish(this.middleware)) { - payload = (await this.middleware.processRequest(payload)); + payload = await this.middleware.processRequest(payload); } if (isWeb3Provider(provider)) { let response; @@ -248,7 +248,7 @@ export class Web3RequestManager< // TODO: This could be deprecated and removed. if (isLegacyRequestProvider(provider)) { return new Promise>((resolve, reject) => { - const rejectWithError = (err: unknown) => + const rejectWithError = (err: unknown) => { reject( this._processJsonRpcResponse( payload, @@ -259,6 +259,8 @@ export class Web3RequestManager< }, ), ); + }; + const resolveWithResponse = (response: JsonRpcResponse) => resolve( this._processJsonRpcResponse(payload, response, { @@ -288,7 +290,20 @@ export class Web3RequestManager< const responsePromise = result as unknown as Promise< JsonRpcResponse >; - responsePromise.then(resolveWithResponse).catch(rejectWithError); + responsePromise.then(resolveWithResponse).catch(error => { + try { + // Attempt to process the error response + const processedError = this._processJsonRpcResponse( + payload, + error as JsonRpcResponse, + { legacy: true, error: true }, + ); + reject(processedError); + } catch (processingError) { + // Catch any errors that occur during the error processing + reject(processingError); + } + }); } }); } diff --git a/packages/web3-core/test/unit/web3_request_manager.test.ts b/packages/web3-core/test/unit/web3_request_manager.test.ts index 375e27a5d1e..2db8c010fd8 100644 --- a/packages/web3-core/test/unit/web3_request_manager.test.ts +++ b/packages/web3-core/test/unit/web3_request_manager.test.ts @@ -280,6 +280,7 @@ describe('Web3RequestManager', () => { expect(myProvider.request).toHaveBeenCalledTimes(1); expect(await pr).toBe('test'); }); + it('Got a "nullish" response from provider', async () => { const manager = new Web3RequestManager(undefined, undefined); const myProvider = { @@ -1134,6 +1135,38 @@ describe('Web3RequestManager', () => { expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); }); + it('should error in isPromise', async () => { + const manager = new Web3RequestManager(); + const myProvider = { + request: jest + .fn() + .mockImplementation(async () => Promise.reject(errorResponse)), + } as any; + + jest.spyOn(manager, 'provider', 'get').mockReturnValue(myProvider); + + await expect(manager.sendBatch(request)).rejects.toEqual(errorResponse); + expect(myProvider.request).toHaveBeenCalledTimes(1); + expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); + }); + + it('should catch error and process json response in isPromise', async () => { + const manager = new Web3RequestManager(); + const myProvider = { + request: jest + .fn() + .mockImplementation(async () => Promise.reject(errorResponse)), + } as any; + jest.spyOn(manager as any, '_processJsonRpcResponse').mockImplementation(() => { + return errorResponse; + }); + jest.spyOn(manager, 'provider', 'get').mockReturnValue(myProvider); + + await expect(manager.sendBatch(request)).rejects.toEqual(errorResponse); + expect(myProvider.request).toHaveBeenCalledTimes(1); + expect(myProvider.request).toHaveBeenCalledWith(payload, expect.any(Function)); + }); + it('should pass request to provider and reject if provider returns error', async () => { const manager = new Web3RequestManager(); const myProvider = { 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 9018381bd6a..9b0eda360dc 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 @@ -90,7 +90,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -119,7 +121,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -152,7 +156,9 @@ describe('Web3Eth.sendTransaction', () => { expect(response.status).toBe(BigInt(1)); expect(response.events).toBeUndefined(); - const minedTransactionData = await web3EthWithWallet.getTransaction(response.transactionHash); + const minedTransactionData = await web3EthWithWallet.getTransaction( + response.transactionHash, + ); expect(minedTransactionData).toMatchObject({ from: tempAcc.address, @@ -534,9 +540,12 @@ describe('Web3Eth.sendTransaction', () => { from: tempAcc.address, data: SimpleRevertDeploymentData, }; - simpleRevertDeployTransaction.gas = await web3Eth.estimateGas(simpleRevertDeployTransaction); - simpleRevertContractAddress = (await web3Eth.sendTransaction(simpleRevertDeployTransaction)) - .contractAddress as Address; + simpleRevertDeployTransaction.gas = await web3Eth.estimateGas( + simpleRevertDeployTransaction, + ); + simpleRevertContractAddress = ( + await web3Eth.sendTransaction(simpleRevertDeployTransaction) + ).contractAddress as Address; }); it('Should throw TransactionRevertInstructionError because gas too low', async () => { @@ -578,7 +587,9 @@ describe('Web3Eth.sendTransaction', () => { const transaction: Transaction = { from: tempAcc.address, to: '0x0000000000000000000000000000000000000000', - value: BigInt('99999999999999999999999999999999999999999999999999999999999999999'), + value: BigInt( + '99999999999999999999999999999999999999999999999999999999999999999', + ), }; const expectedThrownError = { @@ -587,7 +598,9 @@ describe('Web3Eth.sendTransaction', () => { code: 402, reason: getSystemTestBackend() === BACKEND.GETH - ? expect.stringContaining('err: insufficient funds for gas * price + value: address') + ? expect.stringContaining( + 'err: insufficient funds for gas * price + value: address', + ) : 'VM Exception while processing transaction: insufficient balance', }; diff --git a/packages/web3/test/integration/external-providers/hardhat.test.ts b/packages/web3/test/integration/external-providers/hardhat.test.ts index 989424199fb..6a4fb6ff28b 100644 --- a/packages/web3/test/integration/external-providers/hardhat.test.ts +++ b/packages/web3/test/integration/external-providers/hardhat.test.ts @@ -18,11 +18,16 @@ along with web3.js. If not, see . // eslint-disable-next-line import/no-extraneous-dependencies import hardhat from 'hardhat'; -import { performBasicRpcCalls } from './helper'; +import { performBasicRpcCalls, failErrorCalls} from './helper'; describe('compatibility with `hardhat` provider', () => { it('should initialize Web3, get accounts & block number and send a transaction', async () => { // use the hardhat provider for web3.js - await performBasicRpcCalls(hardhat.network.provider); + await expect(performBasicRpcCalls(hardhat.network.provider)).resolves.not.toThrow(); }); + it('should throw on error calls', async () => { + const result = failErrorCalls(hardhat.network.provider); + await expect(result).rejects.toThrow(); + + }) }); diff --git a/packages/web3/test/integration/external-providers/helper.ts b/packages/web3/test/integration/external-providers/helper.ts index c1f88dd86b0..d88b1a7c90f 100644 --- a/packages/web3/test/integration/external-providers/helper.ts +++ b/packages/web3/test/integration/external-providers/helper.ts @@ -16,7 +16,9 @@ along with web3.js. If not, see . */ import { SupportedProviders } from 'web3-types'; +import { Contract } from 'web3-eth-contract'; import Web3 from '../../../src/index'; +import { BasicAbi, BasicBytecode } from '../../shared_fixtures/build/Basic'; /** * Performs basic RPC calls (like `eth_accounts`, `eth_blockNumber` and `eth_sendTransaction`) @@ -36,7 +38,7 @@ export async function performBasicRpcCalls(provider: SupportedProviders) { to: accounts[1], from: accounts[0], value: '1', - gas: 21000 + gas: 21000, }); expect(tx.status).toBe(BigInt(1)); @@ -46,3 +48,27 @@ export async function performBasicRpcCalls(provider: SupportedProviders) { // After sending a transaction, the blocknumber is supposed to be greater than or equal the block number before sending the transaction expect(blockNumber1).toBeGreaterThanOrEqual(blockNumber0); } + +export async function failErrorCalls(provider: SupportedProviders) { + let contract: Contract; + const web3 = new Web3(provider); + + contract = new web3.eth.Contract(BasicAbi, undefined, { + provider, + }); + + let deployOptions: Record; + + // eslint-disable-next-line prefer-const + deployOptions = { + data: BasicBytecode, + arguments: [10, 'string init value'], + }; + const accounts = await web3.eth.getAccounts(); + + const sendOptions = { from: accounts[0], gas: '1000000' }; + + contract = await contract.deploy(deployOptions).send(sendOptions); + + await contract.methods.reverts().send({ from: accounts[0] }); +}