diff --git a/apps/contract-verification/src/app/app.tsx b/apps/contract-verification/src/app/app.tsx index 44d812ae68..3fcee010ea 100644 --- a/apps/contract-verification/src/app/app.tsx +++ b/apps/contract-verification/src/app/app.tsx @@ -22,6 +22,7 @@ const App = () => { const [submittedContracts, setSubmittedContracts] = useLocalStorage('contract-verification:submitted-contracts', {}) const [chains, setChains] = useState([]) // State to hold the chains data const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>() + // Form values: const [selectedChain, setSelectedChain] = useState() const [contractAddress, setContractAddress] = useState('') diff --git a/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx index 5cd834d93a..9ff6474f36 100644 --- a/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx +++ b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx @@ -58,13 +58,14 @@ export const SearchableChainDropdown: React.FC = ({ label, id, se const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false) + setSearchTerm(getChainDescriptor(selectedChain)) } } document.addEventListener('mousedown', handleClickOutside) return () => { document.removeEventListener('mousedown', handleClickOutside) } - }, []) + }, [selectedChain]) const handleInputChange = (e: React.ChangeEvent) => { setSearchTerm(e.target.value) diff --git a/apps/contract-verification/src/app/hooks/useSourcifySupported.tsx b/apps/contract-verification/src/app/hooks/useSourcifySupported.tsx new file mode 100644 index 0000000000..47d4d2437b --- /dev/null +++ b/apps/contract-verification/src/app/hooks/useSourcifySupported.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react' +import { Chain, ChainSettings } from '../types' + +export function useSourcifySupported(selectedChain: Chain, chainSettings: ChainSettings): boolean { + const [sourcifySupported, setSourcifySupported] = useState(false) + + useEffect(() => { + // Unsupported until fetch returns + setSourcifySupported(false) + + const sourcifyApi = chainSettings?.verifiers['Sourcify']?.apiUrl + if (!sourcifyApi) { + return + } + + const queriedChainId = selectedChain.chainId + const chainsUrl = new URL(sourcifyApi + '/chains') + + fetch(chainsUrl.href, { method: 'GET' }) + .then((response) => response.json()) + .then((result: Array<{ chainId: number }>) => { + // Makes sure that the selectedChain didn't change while the request is running + if (selectedChain.chainId === queriedChainId && result.find((chain) => chain.chainId === queriedChainId)) { + setSourcifySupported(true) + } + }) + .catch((error) => { + console.error('Failed to fetch chains.json:', error) + }) + }, [selectedChain, chainSettings]) + + return sourcifySupported +} diff --git a/apps/contract-verification/src/app/views/LookupView.tsx b/apps/contract-verification/src/app/views/LookupView.tsx index b203e575a8..7483ec8101 100644 --- a/apps/contract-verification/src/app/views/LookupView.tsx +++ b/apps/contract-verification/src/app/views/LookupView.tsx @@ -1,13 +1,14 @@ -import { useContext, useEffect, useState } from 'react' +import { useContext, useEffect, useMemo, useState } from 'react' import { SearchableChainDropdown, ContractAddressInput } from '../components' import { mergeChainSettingsWithDefaults, validConfiguration } from '../utils' -import type { LookupResponse, VerifierIdentifier, Chain } from '../types' +import type { LookupResponse, VerifierIdentifier } from '../types' import { VERIFIERS } from '../types' import { AppContext } from '../AppContext' import { CustomTooltip } from '@remix-ui/helper' import { getVerifier } from '../Verifiers' import { useNavigate } from 'react-router-dom' import { VerifyFormContext } from '../VerifyFormContext' +import { useSourcifySupported } from '../hooks/useSourcifySupported' export const LookupView = () => { const { settings, clientInstance } = useContext(AppContext) @@ -18,7 +19,9 @@ export const LookupView = () => { const [lookupResults, setLookupResult] = useState>>({}) const navigate = useNavigate() - const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined + const chainSettings = useMemo(() => (selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined), [selectedChain, settings]) + + const sourcifySupported = useSourcifySupported(selectedChain, chainSettings) const submitDisabled = !!contractAddressError || !contractAddress || !selectedChain @@ -37,7 +40,7 @@ export const LookupView = () => { e.preventDefault() for (const verifierId of VERIFIERS) { - if (!validConfiguration(chainSettings, verifierId)) { + if (!validConfiguration(chainSettings, verifierId) || (verifierId === 'Sourcify' && !sourcifySupported)) { continue } @@ -100,6 +103,21 @@ export const LookupView = () => { ) } + if (verifierId === 'Sourcify' && !sourcifySupported) { + return ( +
+
+ {verifierId}{' '} + + navigate('/settings')}> + Unsupported + + +
+
+ ) + } + return (
diff --git a/apps/contract-verification/src/app/views/SettingsView.tsx b/apps/contract-verification/src/app/views/SettingsView.tsx index df606b31d3..6f0146461f 100644 --- a/apps/contract-verification/src/app/views/SettingsView.tsx +++ b/apps/contract-verification/src/app/views/SettingsView.tsx @@ -1,6 +1,6 @@ -import { useContext, useState } from 'react' +import { useContext, useMemo, useState } from 'react' import { SearchableChainDropdown, ConfigInput } from '../components' -import type { VerifierIdentifier, Chain, VerifierSettings, ContractVerificationSettings } from '../types' +import type { VerifierIdentifier, VerifierSettings, ContractVerificationSettings } from '../types' import { mergeChainSettingsWithDefaults } from '../utils' import { AppContext } from '../AppContext' import { VerifyFormContext } from '../VerifyFormContext' @@ -9,7 +9,7 @@ export const SettingsView = () => { const { settings, setSettings } = useContext(AppContext) const { selectedChain, setSelectedChain } = useContext(VerifyFormContext) - const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined + const chainSettings = useMemo(() => (selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined), [selectedChain, settings]) const handleChange = (verifier: VerifierIdentifier, key: keyof VerifierSettings, value: string) => { const chainId = selectedChain.chainId.toString() diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 8196287fdb..2539a5f4c7 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react' +import { useContext, useEffect, useMemo, useState } from 'react' import { AppContext } from '../AppContext' import { SearchableChainDropdown, ContractDropdown, ContractAddressInput } from '../components' @@ -10,6 +10,7 @@ import { ConstructorArguments } from '../components/ConstructorArguments' import { CustomTooltip } from '@remix-ui/helper' import { AbstractVerifier, getVerifier } from '../Verifiers' import { VerifyFormContext } from '../VerifyFormContext' +import { useSourcifySupported } from '../hooks/useSourcifySupported' export const VerifyView = () => { const { compilationOutput, setSubmittedContracts, settings } = useContext(AppContext) @@ -18,7 +19,9 @@ export const VerifyView = () => { const [hasProxy, setHasProxy] = useState(!!proxyAddress) const navigate = useNavigate() - const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined + const chainSettings = useMemo(() => (selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined), [selectedChain, settings]) + + const sourcifySupported = useSourcifySupported(selectedChain, chainSettings) const submitDisabled = !!contractAddressError || !contractAddress || !selectedChain || !selectedContract || (hasProxy && !!proxyAddressError) || (hasProxy && !proxyAddress) @@ -26,12 +29,12 @@ export const VerifyView = () => { useEffect(() => { const changedEnabledVerifiers = {} for (const verifierId of VERIFIERS) { - if (validConfiguration(chainSettings, verifierId)) { + if (validConfiguration(chainSettings, verifierId) && (verifierId !== 'Sourcify' || sourcifySupported)) { changedEnabledVerifiers[verifierId] = true } } setEnabledVerifiers(changedEnabledVerifiers) - }, [selectedChain]) + }, [selectedChain, sourcifySupported]) const handleVerifierCheckboxClick = (verifierId: VerifierIdentifier, checked: boolean) => { setEnabledVerifiers({ ...enabledVerifiers, [verifierId]: checked }) @@ -174,24 +177,32 @@ export const VerifyView = () => {
Verify on: {VERIFIERS.map((verifierId) => { + const disabledVerifier = !chainSettings || !validConfiguration(chainSettings, verifierId) || (verifierId === 'Sourcify' && !sourcifySupported) + return (
- handleVerifierCheckboxClick(verifierId, e.target.checked)} disabled={!chainSettings || !validConfiguration(chainSettings, verifierId)} /> + handleVerifierCheckboxClick(verifierId, e.target.checked)} disabled={disabledVerifier} />
-