diff --git a/tests/rpc-handler.test.ts b/tests/rpc-handler.test.ts index 7df0441..7427ab2 100644 --- a/tests/rpc-handler.test.ts +++ b/tests/rpc-handler.test.ts @@ -191,4 +191,16 @@ describe("RPCHandler", () => { }, 10000); } }); + + it("Should return the first available RPC", async () => { + const module = await import("../types/rpc-handler"); + const rpcHandler = new module.RPCHandler({ + ...testConfig, + networkId: "1", + rpcTimeout: 1000, + }); + + const provider = await rpcHandler.getFirstAvailableRpcProvider(); + expect(provider).not.toBeNull(); + }); }); diff --git a/types/rpc-handler.ts b/types/rpc-handler.ts index df4d78d..36bad20 100644 --- a/types/rpc-handler.ts +++ b/types/rpc-handler.ts @@ -7,6 +7,13 @@ import { StorageService } from "./storage-service"; const NO_RPCS_AVAILABLE = "No RPCs available"; +function shuffleArray(array: object[]) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } +} + export class RPCHandler implements HandlerInterface { private static _instance: RPCHandler | null = null; private _provider: JsonRpcProvider | null = null; @@ -55,6 +62,27 @@ export class RPCHandler implements HandlerInterface { this.testRpcPerformance.bind(this); } + /** + * Loops through all RPCs for a given network id and returns a provider with the first successful network. + */ + public async getFirstAvailableRpcProvider() { + const rpcList = [...networkRpcs[this._networkId].rpcs]; + shuffleArray(rpcList); + for (const rpc of rpcList) { + try { + const result = await RPCService.makeRpcRequest(rpc.url, this._rpcTimeout, { "Content-Type": "application/json" }); + if (result.success) { + return new JsonRpcProvider({ url: rpc.url, skipFetchSetup: true }, Number(this._networkId)); + } else { + console.error(`Failed to reach endpoint ${rpc.url}. ${result.error}`); + } + } catch (err) { + console.error(`Failed to reach endpoint ${rpc.url}. ${err}`); + } + } + return null; + } + public async getFastestRpcProvider(): Promise { let fastest = await this.testRpcPerformance(); diff --git a/types/rpc-service.ts b/types/rpc-service.ts index 759cf7d..ad7af3b 100644 --- a/types/rpc-service.ts +++ b/types/rpc-service.ts @@ -57,29 +57,8 @@ export class RPCService { throw new Error(rpcUrl); } } - const promises = runtimeRpcs.map((rpcUrl) => requestEndpoint(rpcUrl)); - async function getFirstSuccessfulRequest(requests: string[]) { - if (requests.length === 0) { - throw new Error("All requests failed."); - } - const promisesToResolve = requests.map((rpcUrl) => requestEndpoint(rpcUrl)); - - try { - return await Promise.race(promisesToResolve); - } catch (err) { - console.error(`Failed to reach endpoint. ${err}`); - if (err instanceof Error && requests.includes(err.message)) { - return getFirstSuccessfulRequest(requests.filter((request) => request !== err.message)); - } - return getFirstSuccessfulRequest(requests.slice(1)); - } - } - const fastest = await getFirstSuccessfulRequest(runtimeRpcs); - - if (fastest.success) { - latencies[`${networkId}__${fastest.rpcUrl}`] = fastest.duration; - } + const promises = runtimeRpcs.map((rpcUrl) => requestEndpoint(rpcUrl)); const allResults = await Promise.allSettled(promises); allResults.forEach((result) => {