diff --git a/packages/app-extension/src/components/Unlocked/Settings/Preferences/Blockchains/CustomRpcUrl.tsx b/packages/app-extension/src/components/Unlocked/Settings/Preferences/Blockchains/CustomRpcUrl.tsx index 8cec22fd0..da616c9c1 100644 --- a/packages/app-extension/src/components/Unlocked/Settings/Preferences/Blockchains/CustomRpcUrl.tsx +++ b/packages/app-extension/src/components/Unlocked/Settings/Preferences/Blockchains/CustomRpcUrl.tsx @@ -26,19 +26,56 @@ export function PreferenceBlockchainCustomRpcUrl({ const requiresChainId = blockchainConfig.requiresChainId; const [rpcUrlError, setRpcUrlError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isButtonDisabled, setIsButtonDisabled] = useState(false); useEffect(() => { - if (!rpcUrl) { - setRpcUrlError(false); - return; - } - try { - new URL(rpcUrl.trim()); - setRpcUrlError(false); - } catch (e: any) { - setRpcUrlError(true); - } - }, [rpcUrl]); + setIsButtonDisabled(isLoading || !rpcUrl || rpcUrlError); + }, [isLoading, rpcUrl, rpcUrlError]); + + const verifySolanaRPC = (validUrl: string) => { + setIsLoading(true); + fetch(validUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "getHealth", + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.error) { + throw new Error(data.error.message); + } + setRpcUrlError(false); + setIsLoading(false); + }) + .catch((_error) => { + setRpcUrlError(true); + setIsLoading(false); + }); + }; + + useEffect(() => { + const debounceTimer = setTimeout(() => { + if (!rpcUrl) { + setRpcUrlError(false); + setIsLoading(false); + return; + } + try { + new URL(rpcUrl.trim()); + !requiresChainId && verifySolanaRPC(rpcUrl.trim()); + } catch (e: any) { + setRpcUrlError(true); + setIsLoading(false); + } + }, 500); // Debounce time: 500ms + + return () => clearTimeout(debounceTimer); + }, [rpcUrl, requiresChainId]); return (