diff --git a/apps/contract-verification/src/app/components/ContractAddressInput.tsx b/apps/contract-verification/src/app/components/ContractAddressInput.tsx new file mode 100644 index 0000000000..a537271423 --- /dev/null +++ b/apps/contract-verification/src/app/components/ContractAddressInput.tsx @@ -0,0 +1,33 @@ +import React, { useEffect, useState, useContext } from 'react' +import { ethers } from 'ethers/' + +interface ContractAddressInputProps { + label: string + id: string + setContractAddress: (address: string) => void + contractAddress: string +} + +// Chooses one contract from the compilation output. +export const ContractAddressInput: React.FC = ({ label, id, setContractAddress, contractAddress }) => { + const [contractAddressError, setContractAddressError] = useState('') + + const handleAddressChange = (event: React.ChangeEvent) => { + const isValidAddress = ethers.utils.isAddress(event.target.value) + setContractAddress(event.target.value) + if (!isValidAddress) { + setContractAddressError('Invalid contract address') + console.error('Invalid contract address') + return + } + setContractAddressError('') + } + + return ( +
+ +
{contractAddressError &&
{contractAddressError}
}
+ +
+ ) +} diff --git a/apps/contract-verification/src/app/components/SearchableDropdown.tsx b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx similarity index 67% rename from apps/contract-verification/src/app/components/SearchableDropdown.tsx rename to apps/contract-verification/src/app/components/SearchableChainDropdown.tsx index cb031dcfa9..6959d4c8be 100644 --- a/apps/contract-verification/src/app/components/SearchableDropdown.tsx +++ b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx @@ -1,34 +1,53 @@ -import React, { useState, useEffect, useRef } from 'react' +import React, { useState, useEffect, useRef, useMemo } from 'react' import Fuse from 'fuse.js' import { Chain } from '../types/VerificationTypes' +import { AppContext } from '../AppContext' interface DropdownProps { label: string - chains: Chain[] id: string setSelectedChain: (chain: Chain) => void selectedChain: Chain } -export const SearchableDropdown: React.FC = ({ chains, label, id, setSelectedChain, selectedChain }) => { +export const SearchableChainDropdown: React.FC = ({ label, id, setSelectedChain, selectedChain }) => { + const { chains } = React.useContext(AppContext) + const ethereumChainIds = [1, 3, 4, 5, 11155111, 17000] + + // Add Ethereum chains to the head of the chains list. Sort the rest alphabetically + const dropdownChains = useMemo( + () => + chains.sort((a, b) => { + const isAInEthereum = ethereumChainIds.includes(a.chainId) + const isBInEthereum = ethereumChainIds.includes(b.chainId) + + if (isAInEthereum && !isBInEthereum) return -1 + if (!isAInEthereum && isBInEthereum) return 1 + if (isAInEthereum && isBInEthereum) return ethereumChainIds.indexOf(a.chainId) - ethereumChainIds.indexOf(b.chainId) + + return (a.title || a.name).localeCompare(b.title || b.name) + }), + [chains] + ) + const [searchTerm, setSearchTerm] = useState('') const [isOpen, setIsOpen] = useState(false) - const [filteredOptions, setFilteredOptions] = useState(chains) + const [filteredOptions, setFilteredOptions] = useState(dropdownChains) const dropdownRef = useRef(null) - const fuse = new Fuse(chains, { + const fuse = new Fuse(dropdownChains, { keys: ['name'], threshold: 0.3, }) useEffect(() => { if (searchTerm === '') { - setFilteredOptions(chains) + setFilteredOptions(dropdownChains) } else { const result = fuse.search(searchTerm) setFilteredOptions(result.map(({ item }) => item)) } - }, [searchTerm, chains]) + }, [searchTerm, dropdownChains]) // Close dropdown when user clicks outside useEffect(() => { @@ -59,7 +78,7 @@ export const SearchableDropdown: React.FC = ({ chains, label, id, setSearchTerm('') } - if (!chains || chains.length === 0) { + if (!dropdownChains || dropdownChains.length === 0) { return (
diff --git a/apps/contract-verification/src/app/components/index.tsx b/apps/contract-verification/src/app/components/index.tsx index 4fc1c9e8a0..1aec06fc73 100644 --- a/apps/contract-verification/src/app/components/index.tsx +++ b/apps/contract-verification/src/app/components/index.tsx @@ -1,3 +1,4 @@ export { NavMenu } from './NavMenu' export { ContractDropdown } from './ContractDropdown' -export { SearchableDropdown } from './SearchableDropdown' +export { SearchableChainDropdown } from './SearchableChainDropdown' +export { ContractAddressInput } from './ContractAddressInput' diff --git a/apps/contract-verification/src/app/routes.tsx b/apps/contract-verification/src/app/routes.tsx index e7e665f96d..2e4b78ad08 100644 --- a/apps/contract-verification/src/app/routes.tsx +++ b/apps/contract-verification/src/app/routes.tsx @@ -4,6 +4,7 @@ import { HashRouter as Router, Route, Routes } from 'react-router-dom' import { VerifyView } from './views' import { DefaultLayout } from './layouts' import { ReceiptsView } from './views/ReceiptsView' +import { LookupView } from './views/LookupView' const DisplayRoutes = () => ( @@ -25,6 +26,15 @@ const DisplayRoutes = () => ( } /> + + + + + } + /> ) diff --git a/apps/contract-verification/src/app/views/LookupView.tsx b/apps/contract-verification/src/app/views/LookupView.tsx new file mode 100644 index 0000000000..ab9c448623 --- /dev/null +++ b/apps/contract-verification/src/app/views/LookupView.tsx @@ -0,0 +1,22 @@ +import { useState } from 'react' +import { SearchableChainDropdown, ContractAddressInput } from '../components' +import { Chain } from '../types/VerificationTypes' + +export const LookupView = () => { + const [selectedChain, setSelectedChain] = useState() + const [contractAddress, setContractAddress] = useState('') + + const handleLookup = () => {} + + return ( +
+ + + + + + + ) +} diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 988b9f7bc8..6cb99858fa 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -1,9 +1,7 @@ import React, { useEffect, useState } from 'react' import { AppContext } from '../AppContext' -import { SearchableDropdown } from '../components' -import { ContractDropdown } from '../components/ContractDropdown' -import { ethers } from 'ethers/' +import { SearchableChainDropdown, ContractDropdown, ContractAddressInput } from '../components' import { Chain, SubmittedContract, VerificationReceipt } from '../types/VerificationTypes' import { SourcifyVerifier } from '../Verifiers/SourcifyVerifier' import { EtherscanVerifier } from '../Verifiers/EtherscanVerifier' @@ -11,9 +9,8 @@ import { useNavigate } from 'react-router-dom' import { ConstructorArguments } from '../components/ConstructorArguments' export const VerifyView = () => { - const { chains, compilationOutput, verifiers, setVerifiers, selectedContractFileAndName, setSubmittedContracts } = React.useContext(AppContext) + const { compilationOutput, verifiers, setVerifiers, selectedContractFileAndName, setSubmittedContracts } = React.useContext(AppContext) const [contractAddress, setContractAddress] = useState('') - const [contractAddressError, setContractAddressError] = useState('') const [selectedChain, setSelectedChain] = useState() const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = React.useState('') const navigate = useNavigate() @@ -22,20 +19,6 @@ export const VerifyView = () => { console.log('Selected chain changed', selectedChain) }, [selectedChain]) - const ethereumChainIds = [1, 3, 4, 5, 11155111, 17000] - - // Add Ethereum chains to the head of the chains list. Sort the rest alphabetically - const dropdownChains = chains.sort((a, b) => { - const isAInEthereum = ethereumChainIds.includes(a.chainId) - const isBInEthereum = ethereumChainIds.includes(b.chainId) - - if (isAInEthereum && !isBInEthereum) return -1 - if (!isAInEthereum && isBInEthereum) return 1 - if (isAInEthereum && isBInEthereum) return ethereumChainIds.indexOf(a.chainId) - ethereumChainIds.indexOf(b.chainId) - - return (a.title || a.name).localeCompare(b.title || b.name) - }) - const handleVerify = async (e) => { e.preventDefault() // Don't change the page @@ -96,34 +79,18 @@ export const VerifyView = () => { }) } - const handleAddressChange = (event: React.ChangeEvent) => { - const isValidAddress = ethers.utils.isAddress(event.target.value) - setContractAddress(event.target.value) - if (!isValidAddress) { - setContractAddressError('Invalid contract address') - console.error('Invalid contract address') - return - } - setContractAddressError('') - } - console.log('sourcifyVerifiers:', verifiers) return (
- + -
- -
{contractAddressError &&
{contractAddressError}
}
- -
+