diff --git a/packages/web3-providers-http/src/index.ts b/packages/web3-providers-http/src/index.ts index 64dadd9895d..bd069dbc14c 100644 --- a/packages/web3-providers-http/src/index.ts +++ b/packages/web3-providers-http/src/index.ts @@ -78,9 +78,10 @@ export default class HttpProvider< }, body: JSON.stringify(payload), }); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (!response.ok) throw new ResponseError(await response.json()); + if (!response.ok) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + throw new ResponseError(await response.json()) + }; return (await response.json()) as JsonRpcResponseWithResult; } diff --git a/packages/web3-rpc-providers/CHANGELOG.md b/packages/web3-rpc-providers/CHANGELOG.md index 1909cc7a509..78d7c02f440 100644 --- a/packages/web3-rpc-providers/CHANGELOG.md +++ b/packages/web3-rpc-providers/CHANGELOG.md @@ -42,3 +42,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RC release ## [Unreleased] + - When error is returned with code 429, throw rate limit error (#7102) + - Change request return type `Promise` to `Promise>` (#7102) \ No newline at end of file diff --git a/packages/web3-rpc-providers/package.json b/packages/web3-rpc-providers/package.json index 6904037773a..8aed22fe547 100644 --- a/packages/web3-rpc-providers/package.json +++ b/packages/web3-rpc-providers/package.json @@ -56,8 +56,10 @@ "typescript": "^4.7.4" }, "dependencies": { + "web3-errors": "^1.2.0", "web3-providers-http": "^4.1.0", "web3-providers-ws": "^4.0.7", + "web3-validator": "^2.0.6", "web3-types": "^1.7.0", "web3-utils": "^4.3.0" } diff --git a/packages/web3-rpc-providers/src/errors.ts b/packages/web3-rpc-providers/src/errors.ts new file mode 100644 index 00000000000..dd602fcb05f --- /dev/null +++ b/packages/web3-rpc-providers/src/errors.ts @@ -0,0 +1,28 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +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 { BaseWeb3Error } from 'web3-errors'; +import { } from 'web3-types'; + +const ERR_QUICK_NODE_RATE_LIMIT = 1300; +export class QuickNodeRateLimitError extends BaseWeb3Error { + public code = ERR_QUICK_NODE_RATE_LIMIT; + + public constructor() { + super(`Too many requests, Quicknode has reached its rate limit.`); + } +} \ No newline at end of file diff --git a/packages/web3-rpc-providers/src/web3_provider.ts b/packages/web3-rpc-providers/src/web3_provider.ts index 2bba9a7b880..a00034048ef 100644 --- a/packages/web3-rpc-providers/src/web3_provider.ts +++ b/packages/web3-rpc-providers/src/web3_provider.ts @@ -17,16 +17,19 @@ along with web3.js. If not, see . import HttpProvider from "web3-providers-http"; import WebSocketProvider from "web3-providers-ws"; +import { isNullish } from "web3-validator"; import { EthExecutionAPI, JsonRpcResult, ProviderConnectInfo, ProviderMessage, ProviderRpcError, Web3APIMethod, Web3APIPayload, Web3APIReturnType, Web3APISpec, Web3BaseProvider, Web3Eip1193ProviderEventCallback, Web3ProviderEventCallback, Web3ProviderMessageEventCallback, - Web3ProviderStatus + Web3ProviderStatus, + JsonRpcResponseWithResult, } from "web3-types"; import { Eip1193Provider } from "web3-utils"; import { Transport, Network } from "./types.js"; +import { QuickNodeRateLimitError } from './errors.js'; /* This class can be used to create new providers only when there is custom logic required in each Request method like @@ -68,13 +71,20 @@ API extends Web3APISpec = EthExecutionAPI, >( payload: Web3APIPayload, requestOptions?: RequestInit, - ): Promise { + ): Promise> { if (this.transport === Transport.HTTPS) { - return ( (this.provider as HttpProvider).request(payload, requestOptions)) as unknown as Promise; - } + const res = await ( (this.provider as HttpProvider).request(payload, requestOptions)) as unknown as JsonRpcResponseWithResult; + + if (typeof res === 'object' && !isNullish(res) && 'error' in res && !isNullish(res.error) && 'code' in res.error && (res.error as { code: number }).code === 429){ + // rate limiting error by quicknode; + throw new QuickNodeRateLimitError(); + + } + return res; + } - return ( (this.provider as WebSocketProvider).request(payload)) as unknown as Promise; + return (this.provider as WebSocketProvider).request(payload); } diff --git a/packages/web3-rpc-providers/test/unit/request.test.ts b/packages/web3-rpc-providers/test/unit/request.test.ts index ccbaa0f7cc7..d0d38ac5e14 100644 --- a/packages/web3-rpc-providers/test/unit/request.test.ts +++ b/packages/web3-rpc-providers/test/unit/request.test.ts @@ -17,6 +17,7 @@ along with web3.js. If not, see . import { Web3APIPayload, EthExecutionAPI, Web3APIMethod } from "web3-types"; import { Network, Transport } from "../../src/types"; import { Web3ExternalProvider } from "../../src/web3_provider"; +import { QuickNodeRateLimitError } from '../../src/errors'; jest.mock('web3-providers-ws', () => { return { @@ -78,4 +79,32 @@ describe('Web3ExternalProvider', () => { const result = await provider.request(payload); expect(result).toEqual({ result: 'mock-result' }); }); + it('should return a rate limiting error when code is 429', async () => { + const network: Network = Network.ETH_MAINNET; + const transport: Transport = Transport.HTTPS; + const token = 'your-token'; + + const mockHttpProvider = { + request: jest.fn(), + }; + + const mockResponse = { + jsonrpc: '2.0', + id: '458408f4-7e2c-43f1-b61d-1fe09a9ee25a', + error: { + code: 429, + message: 'the method eth_stuff does not exist/is not available' + } + }; + mockHttpProvider.request.mockResolvedValue(mockResponse); + + const provider = new MockWeb3ExternalProvider(network, transport, token); + (provider as any).provider = mockHttpProvider; + + const payload: Web3APIPayload> = { + method: 'eth_getBalance', + params: ['0x0123456789012345678901234567890123456789', 'latest'], + }; + await expect(provider.request(payload)).rejects.toThrow(QuickNodeRateLimitError); + }); }); \ No newline at end of file