From 4096ebf40df3959f17e0e9a587b2d2c7c906faf6 Mon Sep 17 00:00:00 2001 From: mubarakone <52806204+mubarakone@users.noreply.github.com> Date: Fri, 18 Jul 2025 19:14:01 -0700 Subject: [PATCH 1/4] Update index.js - issue 1674 fix --- components/RPCList/index.js | 141 +++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/components/RPCList/index.js b/components/RPCList/index.js index 20f9455b61..f12f49ef23 100644 --- a/components/RPCList/index.js +++ b/components/RPCList/index.js @@ -11,13 +11,97 @@ import { Tooltip } from "../../components/Tooltip"; import useAccount from "../../hooks/useAccount"; import { Popover, PopoverDisclosure, usePopoverStore } from "@ariakit/react/popover"; +// Test functions for trace and archive support +const testTraceSupport = async (rpcUrl) => { + const payload = { + jsonrpc: "2.0", + method: "debug_traceTransaction", + params: [ + "0x5a5efc6dd80fd85b291d0c2f79a1c88f2cb36aa9e5bd1951972e8b4cf5d9b17b" + ], + id: 1, + }; + + try { + const response = await fetch(rpcUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + return data.result ? "supported" : "not-supported"; + } catch (error) { + return "error"; + } +}; + +const testArchiveSupport = async (rpcUrl) => { + const payload = { + jsonrpc: "2.0", + method: "eth_getBalance", + params: [ + "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", + "0x0" + ], + id: 1, + }; + + try { + const response = await fetch(rpcUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + return data.result ? "supported" : "not-supported"; + } catch (error) { + return "error"; + } +}; + export default function RPCList({ chain, lang }) { const [sortChains, setSorting] = useState(true); + const [supportCache, setSupportCache] = useState({}); const urlToData = chain.rpc.reduce((all, c) => ({ ...all, [c.url]: c }), {}); const chains = useRPCData(chain.rpc); + // Test trace and archive support for each RPC + useEffect(() => { + const testSupport = async () => { + const newCache = { ...supportCache }; + + for (const chain of chains || []) { + if (chain.data?.url && !supportCache[chain.data.url]) { + newCache[chain.data.url] = { + trace: "testing", + archive: "testing" + }; + + // Test both features in parallel + const [traceResult, archiveResult] = await Promise.all([ + testTraceSupport(chain.data.url), + testArchiveSupport(chain.data.url) + ]); + + newCache[chain.data.url] = { + trace: traceResult, + archive: archiveResult + }; + } + } + + setSupportCache(newCache); + }; + + if (chains?.length > 0) { + testSupport(); + } + }, [chains]); + const data = useMemo(() => { const sortedData = sortChains ? chains?.sort((a, b) => { @@ -70,12 +154,23 @@ export default function RPCList({ chain, lang }) { const lat = latency ? (latency / 1000).toFixed(3) + "s" : null; + // Add trace and archive support data + const support = supportCache[url] || { trace: "unknown", archive: "unknown" }; + return { ...rest, - data: { ...data, height, latency: lat, trust, disableConnect }, + data: { + ...data, + height, + latency: lat, + trust, + disableConnect, + traceSupport: support.trace, + archiveSupport: support.archive + }, }; }); - }, [chains]); + }, [chains, supportCache]); const { rpcData, hasLlamaNodesRpc } = useLlamaNodesRpcData(chain.chainId, data); @@ -113,6 +208,8 @@ export default function RPCList({ chain, lang }) { Latency Score Privacy + Trace + Archive @@ -163,6 +260,21 @@ function PrivacyIcon({ tracking, isOpenSource = false }) { return ; } +function SupportIcon({ support }) { + switch (support) { + case "supported": + return ; + case "not-supported": + return ; + case "error": + return ; + case "testing": + return
; + default: + return ; + } +} + const Row = ({ values, chain, privacy, lang, className }) => { const t = useTranslations("Common", lang); const { data, isLoading, refetch } = values; @@ -184,6 +296,21 @@ const Row = ({ values, chain, privacy, lang, className }) => { const { mutate: addToNetwork } = useAddToNetwork(); + const getTooltipContent = (support, type) => { + switch (support) { + case "supported": + return `${type} methods are supported`; + case "not-supported": + return `${type} methods are not supported`; + case "error": + return `Error testing ${type} support`; + case "testing": + return `Testing ${type} support...`; + default: + return `${type} support unknown`; + } + }; + return ( @@ -211,6 +338,16 @@ const Row = ({ values, chain, privacy, lang, className }) => { {isLoading ? : } + + + {isLoading ? : } + + + + + {isLoading ? : } + + {isLoading ? ( From 7c60ca5dbc178550d89dd143ebe7c773b1928e8f Mon Sep 17 00:00:00 2001 From: mintdart <96025197+mintdart@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:12:02 +0900 Subject: [PATCH 2/4] refactor --- components/RPCList/index.js | 75 ++++++++++++++----------------------- 1 file changed, 29 insertions(+), 46 deletions(-) diff --git a/components/RPCList/index.js b/components/RPCList/index.js index f12f49ef23..a0579a400e 100644 --- a/components/RPCList/index.js +++ b/components/RPCList/index.js @@ -10,6 +10,7 @@ import { renderProviderText } from "../../utils"; import { Tooltip } from "../../components/Tooltip"; import useAccount from "../../hooks/useAccount"; import { Popover, PopoverDisclosure, usePopoverStore } from "@ariakit/react/popover"; +import { useQuery } from "@tanstack/react-query"; // Test functions for trace and archive support const testTraceSupport = async (rpcUrl) => { @@ -63,45 +64,11 @@ const testArchiveSupport = async (rpcUrl) => { export default function RPCList({ chain, lang }) { const [sortChains, setSorting] = useState(true); - const [supportCache, setSupportCache] = useState({}); const urlToData = chain.rpc.reduce((all, c) => ({ ...all, [c.url]: c }), {}); const chains = useRPCData(chain.rpc); - // Test trace and archive support for each RPC - useEffect(() => { - const testSupport = async () => { - const newCache = { ...supportCache }; - - for (const chain of chains || []) { - if (chain.data?.url && !supportCache[chain.data.url]) { - newCache[chain.data.url] = { - trace: "testing", - archive: "testing" - }; - - // Test both features in parallel - const [traceResult, archiveResult] = await Promise.all([ - testTraceSupport(chain.data.url), - testArchiveSupport(chain.data.url) - ]); - - newCache[chain.data.url] = { - trace: traceResult, - archive: archiveResult - }; - } - } - - setSupportCache(newCache); - }; - - if (chains?.length > 0) { - testSupport(); - } - }, [chains]); - const data = useMemo(() => { const sortedData = sortChains ? chains?.sort((a, b) => { @@ -154,9 +121,6 @@ export default function RPCList({ chain, lang }) { const lat = latency ? (latency / 1000).toFixed(3) + "s" : null; - // Add trace and archive support data - const support = supportCache[url] || { trace: "unknown", archive: "unknown" }; - return { ...rest, data: { @@ -164,13 +128,11 @@ export default function RPCList({ chain, lang }) { height, latency: lat, trust, - disableConnect, - traceSupport: support.trace, - archiveSupport: support.archive + disableConnect }, }; }); - }, [chains, supportCache]); + }, [chains]); const { rpcData, hasLlamaNodesRpc } = useLlamaNodesRpcData(chain.chainId, data); @@ -296,7 +258,28 @@ const Row = ({ values, chain, privacy, lang, className }) => { const { mutate: addToNetwork } = useAddToNetwork(); - const getTooltipContent = (support, type) => { + + const traceSupport = useQuery({ + queryKey: ["support", data?.url], + queryFn: () => testTraceSupport(data?.url), + staleTime: 1000 * 60 * 60, + refetchInterval: 1000 * 60 * 60, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: !!data?.url, + }) + + const archiveSupport = useQuery({ + queryKey: ["support", data?.url], + queryFn: () => testArchiveSupport(data?.url), + staleTime: 1000 * 60 * 60, + refetchInterval: 1000 * 60 * 60, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: !!data?.url, + }) + + const getSupportTooltipContent = (support, type) => { switch (support) { case "supported": return `${type} methods are supported`; @@ -339,13 +322,13 @@ const Row = ({ values, chain, privacy, lang, className }) => { - - {isLoading ? : } + + {isLoading ? : } - - {isLoading ? : } + + {isLoading ? : } From 7f8e437aabbb5773d44fd6b19659eaa5f16fb571 Mon Sep 17 00:00:00 2001 From: mubarakone <52806204+mubarakone@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:48:02 -0700 Subject: [PATCH 3/4] fix tx not found error - tx not found means debug_traceTransaction method is found, which is good --- components/RPCList/index.js | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/components/RPCList/index.js b/components/RPCList/index.js index a0579a400e..ded5a37fe6 100644 --- a/components/RPCList/index.js +++ b/components/RPCList/index.js @@ -13,50 +13,52 @@ import { Popover, PopoverDisclosure, usePopoverStore } from "@ariakit/react/popo import { useQuery } from "@tanstack/react-query"; // Test functions for trace and archive support + const testTraceSupport = async (rpcUrl) => { - const payload = { + // Test trace support (with fake tx hash) + const tracePayload = { jsonrpc: "2.0", method: "debug_traceTransaction", - params: [ - "0x5a5efc6dd80fd85b291d0c2f79a1c88f2cb36aa9e5bd1951972e8b4cf5d9b17b" - ], + params: ["0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", {}], id: 1, }; try { - const response = await fetch(rpcUrl, { + + const traceRes = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); + body: JSON.stringify(tracePayload), + }).then(r => r.json()); + + const traceSupported = !traceRes.error?.message.includes("method" || "API"); + return traceSupported ? "supported" : "not-supported"; - const data = await response.json(); - return data.result ? "supported" : "not-supported"; } catch (error) { return "error"; } }; const testArchiveSupport = async (rpcUrl) => { - const payload = { + // Test archive support + const archivePayload = { jsonrpc: "2.0", method: "eth_getBalance", - params: [ - "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", - "0x0" - ], + params: ["0x0000000000000000000000000000000000000000", "0x1"], id: 1, }; try { - const response = await fetch(rpcUrl, { + + const archiveRes = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); + body: JSON.stringify(archivePayload), + }).then(r => r.json()); - const data = await response.json(); - return data.result ? "supported" : "not-supported"; + const archiveSupported = !!archiveRes.result; + return archiveSupported ? "supported" : "not-supported"; + } catch (error) { return "error"; } From 39003e377998c3b965fd376c3c08ee2307cca560 Mon Sep 17 00:00:00 2001 From: caranell Date: Wed, 17 Sep 2025 00:23:33 +0300 Subject: [PATCH 4/4] Fixes to archive/trace support --- components/RPCList/index.js | 121 +++++++++++++++++++++++++----------- utils/index.js | 5 ++ 2 files changed, 88 insertions(+), 38 deletions(-) diff --git a/components/RPCList/index.js b/components/RPCList/index.js index ded5a37fe6..4c8fc98ea5 100644 --- a/components/RPCList/index.js +++ b/components/RPCList/index.js @@ -6,36 +6,55 @@ import useAddToNetwork from "../../hooks/useAddToNetwork"; import { useLlamaNodesRpcData } from "../../hooks/useLlamaNodesRpcData"; import { FATHOM_DROPDOWN_EVENTS_ID } from "../../hooks/useAnalytics"; import { useRpcStore } from "../../stores"; -import { renderProviderText } from "../../utils"; +import { renderProviderText, containsAny } from "../../utils"; import { Tooltip } from "../../components/Tooltip"; import useAccount from "../../hooks/useAccount"; import { Popover, PopoverDisclosure, usePopoverStore } from "@ariakit/react/popover"; import { useQuery } from "@tanstack/react-query"; -// Test functions for trace and archive support +// Functions to test trace and archive support + +const SUPPORT_STATUS = { + supported: "supported", + not_supported: "not-supported", + error: "error", + testing: "testing", + unknown: "unknown", +}; const testTraceSupport = async (rpcUrl) => { // Test trace support (with fake tx hash) + + const fakeTxHash = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; const tracePayload = { jsonrpc: "2.0", method: "debug_traceTransaction", - params: ["0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", {}], + params: [fakeTxHash, {}], id: 1, }; try { - const traceRes = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(tracePayload), - }).then(r => r.json()); + signal: AbortSignal.timeout(7000), + }).then((r) => r.json()); + + const errorMessage = traceRes?.error?.message || traceRes?.message; + + // some RPCs might allow the trace with restrictions, better marks those as 'failed to test' instead of 'not supported' + if (containsAny(errorMessage, ["auth", "rate", "limit", "api", "allowed", "whitelist", "origin"])) { + throw new Error(errorMessage); + } - const traceSupported = !traceRes.error?.message.includes("method" || "API"); - return traceSupported ? "supported" : "not-supported"; + const traceSupported = + containsAny(errorMessage, ["transaction not found", `transaction ${fakeTxHash} not found`]) || + traceRes?.result === null; + return traceSupported ? SUPPORT_STATUS.supported : SUPPORT_STATUS.not_supported; } catch (error) { - return "error"; + return SUPPORT_STATUS.error; } }; @@ -49,18 +68,17 @@ const testArchiveSupport = async (rpcUrl) => { }; try { - const archiveRes = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(archivePayload), - }).then(r => r.json()); + signal: AbortSignal.timeout(7000), + }).then((r) => r.json()); - const archiveSupported = !!archiveRes.result; - return archiveSupported ? "supported" : "not-supported"; - + const archiveSupported = Boolean(archiveRes.result); + return archiveSupported ? SUPPORT_STATUS.supported : SUPPORT_STATUS.not_supported; } catch (error) { - return "error"; + return SUPPORT_STATUS.error; } }; @@ -125,12 +143,12 @@ export default function RPCList({ chain, lang }) { return { ...rest, - data: { - ...data, - height, - latency: lat, - trust, - disableConnect + data: { + ...data, + height, + latency: lat, + trust, + disableConnect, }, }; }); @@ -226,14 +244,16 @@ function PrivacyIcon({ tracking, isOpenSource = false }) { function SupportIcon({ support }) { switch (support) { - case "supported": + case SUPPORT_STATUS.supported: return ; - case "not-supported": + case SUPPORT_STATUS.not_supported: return ; - case "error": + case SUPPORT_STATUS.error: return ; - case "testing": - return
; + case SUPPORT_STATUS.testing: + return ( +
+ ); default: return ; } @@ -260,37 +280,38 @@ const Row = ({ values, chain, privacy, lang, className }) => { const { mutate: addToNetwork } = useAddToNetwork(); - const traceSupport = useQuery({ - queryKey: ["support", data?.url], + queryKey: ["trace-support", data?.url], queryFn: () => testTraceSupport(data?.url), staleTime: 1000 * 60 * 60, refetchInterval: 1000 * 60 * 60, refetchOnMount: false, refetchOnWindowFocus: false, enabled: !!data?.url, - }) + }); const archiveSupport = useQuery({ - queryKey: ["support", data?.url], + queryKey: ["archive-support", data?.url], queryFn: () => testArchiveSupport(data?.url), staleTime: 1000 * 60 * 60, refetchInterval: 1000 * 60 * 60, refetchOnMount: false, refetchOnWindowFocus: false, enabled: !!data?.url, - }) + }); const getSupportTooltipContent = (support, type) => { switch (support) { - case "supported": + case SUPPORT_STATUS.supported: return `${type} methods are supported`; - case "not-supported": + case SUPPORT_STATUS.not_supported: return `${type} methods are not supported`; - case "error": + case SUPPORT_STATUS.error: return `Error testing ${type} support`; - case "testing": + case SUPPORT_STATUS.testing: return `Testing ${type} support...`; + case SUPPORT_STATUS.unknown: + return `${type} support unknown`; default: return `${type} support unknown`; } @@ -324,13 +345,37 @@ const Row = ({ values, chain, privacy, lang, className }) => {
- - {isLoading ? : } + + {isLoading ? ( + + ) : ( + + )} - - {isLoading ? : } + + {isLoading ? ( + + ) : ( + + )} diff --git a/utils/index.js b/utils/index.js index 4e2213fd39..77e7827bdd 100644 --- a/utils/index.js +++ b/utils/index.js @@ -94,3 +94,8 @@ export const notTranslation = return en[ns][key]; } }; + +export const containsAny = (str, substrings) => { + if (!str) return false; + return substrings.some((substring) => str.toLowerCase().includes(substring.toLowerCase())); +};