From e9d65617a43e45612ebeb5e57d57a0db7a94ff43 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 26 Jul 2024 12:30:46 +0200 Subject: [PATCH 1/5] Poll RPCs on chain page --- src/components/RpcTable.tsx | 75 +++++++++++++++++++++++------ src/pages/chain/{Chain.chainId}.tsx | 2 +- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/components/RpcTable.tsx b/src/components/RpcTable.tsx index f2b3c09a2..ea2faaccd 100644 --- a/src/components/RpcTable.tsx +++ b/src/components/RpcTable.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Button, Table, @@ -10,9 +10,46 @@ import { Tr, } from "@chakra-ui/react"; import { Web3Context } from "../context/Web3Context"; +import { JsonRpcProvider } from "ethers"; +import { ChainData } from "../types/chain"; -export const RpcTable = ({ rpcs, handleRpcClick }) => { +async function checkRpc(chainId: number, rpc: string) { + try { + const now = Date.now(); + const provider = new JsonRpcProvider(rpc, chainId, { staticNetwork: true }); + const blockNumber = await provider.getBlockNumber(); + return { blockNumber, latency: Date.now() - now }; + } catch { + return null; + } +} + +export const RpcTable = ({ + chainId, + rpcs, + handleRpcClick, +}: Pick & { rpcs: ChainData["rpc"] }) => { const { isConnected, handleConnect } = useContext(Web3Context); + const [rpcResults, setRpcResults] = useState(null); + + useEffect(() => { + Promise.all(rpcs.map((rpc) => checkRpc(chainId, rpc))).then(setRpcResults); + }, []); + + const mergedRpcs = rpcs + .map((rpcUrl, index) => { + const rpcResult = rpcResults?.[index] ?? {}; + return { rpcUrl, ...rpcResult }; + }) + .sort((a, b) => { + if (!a.latency) { + return 1; + } + if (!b.latency) { + return -1; + } + return a.latency < b.latency ? -1 : 1; + }); return ( @@ -20,24 +57,30 @@ export const RpcTable = ({ rpcs, handleRpcClick }) => { RPC URL + Block Number + Latency - {rpcs.map((rpcUrl) => ( - - {rpcUrl} - - {!isConnected ? ( - - ) : ( - - )} - - - ))} + {mergedRpcs.map(({ rpcUrl, blockNumber, latency }) => { + return ( + + {rpcUrl} + {blockNumber ?? "?"} + {latency ?? "?"} ms + + {!isConnected ? ( + + ) : ( + + )} + + + ); + })} diff --git a/src/pages/chain/{Chain.chainId}.tsx b/src/pages/chain/{Chain.chainId}.tsx index bff377bdd..1056dd929 100644 --- a/src/pages/chain/{Chain.chainId}.tsx +++ b/src/pages/chain/{Chain.chainId}.tsx @@ -117,7 +117,7 @@ const ChainPage = ({ data }: { data: { chain: ChainData } }) => { )} - + From d3f0c6d660cdf952f6b846260913f2831180983b Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 26 Jul 2024 13:08:22 +0200 Subject: [PATCH 2/5] Log errors temporarily --- src/components/RpcTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/RpcTable.tsx b/src/components/RpcTable.tsx index ea2faaccd..5fa633a2b 100644 --- a/src/components/RpcTable.tsx +++ b/src/components/RpcTable.tsx @@ -19,7 +19,8 @@ async function checkRpc(chainId: number, rpc: string) { const provider = new JsonRpcProvider(rpc, chainId, { staticNetwork: true }); const blockNumber = await provider.getBlockNumber(); return { blockNumber, latency: Date.now() - now }; - } catch { + } catch (error) { + console.error(error); return null; } } From 0916e67f233c928f1f953374586b5b7765d21005 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 26 Jul 2024 14:06:52 +0200 Subject: [PATCH 3/5] Revert --- src/components/RpcTable.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/RpcTable.tsx b/src/components/RpcTable.tsx index 5fa633a2b..ea2faaccd 100644 --- a/src/components/RpcTable.tsx +++ b/src/components/RpcTable.tsx @@ -19,8 +19,7 @@ async function checkRpc(chainId: number, rpc: string) { const provider = new JsonRpcProvider(rpc, chainId, { staticNetwork: true }); const blockNumber = await provider.getBlockNumber(); return { blockNumber, latency: Date.now() - now }; - } catch (error) { - console.error(error); + } catch { return null; } } From 5d22525771ae44e840b87ac7e02c276ff427f1f9 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 26 Jul 2024 14:21:39 +0200 Subject: [PATCH 4/5] Improve formatting slightly --- src/components/RpcTable.tsx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/RpcTable.tsx b/src/components/RpcTable.tsx index ea2faaccd..daf1d2418 100644 --- a/src/components/RpcTable.tsx +++ b/src/components/RpcTable.tsx @@ -1,6 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; import { Button, + Skeleton, Table, TableContainer, Tbody, @@ -19,8 +20,8 @@ async function checkRpc(chainId: number, rpc: string) { const provider = new JsonRpcProvider(rpc, chainId, { staticNetwork: true }); const blockNumber = await provider.getBlockNumber(); return { blockNumber, latency: Date.now() - now }; - } catch { - return null; + } catch (error) { + return { error }; } } @@ -63,12 +64,24 @@ export const RpcTable = ({ - {mergedRpcs.map(({ rpcUrl, blockNumber, latency }) => { + {mergedRpcs.map(({ rpcUrl, blockNumber, latency, error }) => { return ( {rpcUrl} - {blockNumber ?? "?"} - {latency ?? "?"} ms + + {blockNumber || error ? ( + blockNumber ?? "Unavailable" + ) : ( + + )} + + + {latency || error ? ( + <>{latency ?? "?"} ms + ) : ( + + )} + {!isConnected ? ( From 119df42b16e13b0c8cd1ed07946ae11244810f5b Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 26 Jul 2024 14:32:14 +0200 Subject: [PATCH 5/5] Populate RPC responses as they are received --- src/components/RpcTable.tsx | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/components/RpcTable.tsx b/src/components/RpcTable.tsx index daf1d2418..0b1a9dd72 100644 --- a/src/components/RpcTable.tsx +++ b/src/components/RpcTable.tsx @@ -14,14 +14,23 @@ import { Web3Context } from "../context/Web3Context"; import { JsonRpcProvider } from "ethers"; import { ChainData } from "../types/chain"; -async function checkRpc(chainId: number, rpc: string) { +interface RpcResult { + rpcUrl: string; + blockNumber?: number; + latency?: number; + error?: unknown; +} + +async function checkRpc(chainId: number, rpcUrl: string): Promise { try { const now = Date.now(); - const provider = new JsonRpcProvider(rpc, chainId, { staticNetwork: true }); + const provider = new JsonRpcProvider(rpcUrl, chainId, { + staticNetwork: true, + }); const blockNumber = await provider.getBlockNumber(); - return { blockNumber, latency: Date.now() - now }; + return { rpcUrl, blockNumber, latency: Date.now() - now }; } catch (error) { - return { error }; + return { rpcUrl, error }; } } @@ -29,18 +38,25 @@ export const RpcTable = ({ chainId, rpcs, handleRpcClick, -}: Pick & { rpcs: ChainData["rpc"] }) => { +}: Pick & { + rpcs: ChainData["rpc"]; + handleRpcClick: (rpc: string) => void; +}) => { const { isConnected, handleConnect } = useContext(Web3Context); - const [rpcResults, setRpcResults] = useState(null); + const [rpcResults, setRpcResults] = useState(null); useEffect(() => { - Promise.all(rpcs.map((rpc) => checkRpc(chainId, rpc))).then(setRpcResults); + rpcs.forEach((rpc) => + checkRpc(chainId, rpc).then((result) => { + setRpcResults((state) => [...(state ?? []), result]); + }) + ); }, []); const mergedRpcs = rpcs - .map((rpcUrl, index) => { - const rpcResult = rpcResults?.[index] ?? {}; - return { rpcUrl, ...rpcResult }; + .map((rpcUrl) => { + const rpcResult = rpcResults?.find((result) => result.rpcUrl === rpcUrl); + return rpcResult ?? { rpcUrl }; }) .sort((a, b) => { if (!a.latency) {