Make VerifyView functional with modified Verifier classes

pull/5285/head
Manuel Wedler 4 months ago committed by Aniket
parent b1972669ec
commit 6c98d1a424
  1. 56
      apps/contract-verification/src/app/app.tsx
  2. 2
      apps/contract-verification/src/app/components/AccordionReceipt.tsx
  3. 2
      apps/contract-verification/src/app/components/NavMenu.tsx
  4. 13
      apps/contract-verification/src/app/hooks/useLocalStorage.tsx
  5. 7
      apps/contract-verification/src/app/types/VerificationTypes.ts
  6. 6
      apps/contract-verification/src/app/types/defaults.ts
  7. 15
      apps/contract-verification/src/app/views/LookupView.tsx
  8. 9
      apps/contract-verification/src/app/views/ReceiptsView.tsx
  9. 118
      apps/contract-verification/src/app/views/VerifyView.tsx
  10. 104
      apps/contract-verification/src/app/views/example.js

@ -1,14 +1,15 @@
import { useState, useEffect } from 'react' import { useState, useEffect, useRef } from 'react'
import { ContractVerificationPluginClient } from './ContractVerificationPluginClient' import { ContractVerificationPluginClient } from './ContractVerificationPluginClient'
import { AppContext } from './AppContext' import { AppContext } from './AppContext'
import DisplayRoutes from './routes' import DisplayRoutes from './routes'
import { ContractVerificationSettings, ThemeType, Chain, SubmittedContracts } from './types' import { ContractVerificationSettings, ThemeType, Chain, SubmittedContracts, VerificationReceipt, mergeChainSettingsWithDefaults } from './types'
import './App.css' import './App.css'
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
import { useLocalStorage } from './hooks/useLocalStorage' import { useLocalStorage } from './hooks/useLocalStorage'
import { getVerifier } from './Verifiers'
const plugin = new ContractVerificationPluginClient() const plugin = new ContractVerificationPluginClient()
@ -19,6 +20,7 @@ const App = () => {
// TODO: Types for chains // TODO: Types for chains
const [chains, setChains] = useState<Chain[]>([]) // State to hold the chains data const [chains, setChains] = useState<Chain[]>([]) // State to hold the chains data
const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>() const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>()
const timer = useRef(null)
useEffect(() => { useEffect(() => {
// TODO: Fix 'compilationFinished' event types. The interface is outdated at https://github.com/ethereum/remix-plugin/blob/master/packages/api/src/lib/compiler/api.ts. It does not include data, input, or version. See the current parameters: https://github.com/ethereum/remix-project/blob/9f6c5be882453a555055f07171701459e4ae88a4/libs/remix-solidity/src/compiler/compiler.ts#L189 // TODO: Fix 'compilationFinished' event types. The interface is outdated at https://github.com/ethereum/remix-plugin/blob/master/packages/api/src/lib/compiler/api.ts. It does not include data, input, or version. See the current parameters: https://github.com/ethereum/remix-project/blob/9f6c5be882453a555055f07171701459e4ae88a4/libs/remix-solidity/src/compiler/compiler.ts#L189
@ -51,6 +53,56 @@ const App = () => {
} }
}, []) }, [])
// Poll status of pending receipts frequently
useEffect(() => {
const getPendingReceipts = (submissions: SubmittedContracts) => {
const pendingReceipts: VerificationReceipt[] = []
// Check statuses of receipts
for (const submission of Object.values(submissions)) {
for (const receipt of submission.receipts) {
if (receipt.status === 'pending' && receipt.verifierInfo.name !== 'Sourcify') {
pendingReceipts.push(receipt)
}
}
}
return pendingReceipts
}
let pendingReceipts = getPendingReceipts(submittedContracts)
if (pendingReceipts.length > 0) {
if (timer.current) {
clearInterval(timer.current)
timer.current = null
}
timer.current = setInterval(async () => {
const changedSubmittedContracts = { ...submittedContracts }
for (const receipt of pendingReceipts) {
await new Promise((resolve) => setTimeout(resolve, 500)) // avoid api rate limit exceed.
const { verifierInfo, receiptId } = receipt
if (receiptId) {
const contract = changedSubmittedContracts[receipt.contractId]
const chainSettings = mergeChainSettingsWithDefaults(contract.chainId, settings)
const verifierSettings = chainSettings.verifiers[verifierInfo.name]
// In case the user overwrites the API later, prefer the one stored in localStorage
const verifier = getVerifier(verifierInfo.name, { ...verifierSettings, apiUrl: verifierInfo.apiUrl })
receipt.status = await verifier.checkVerificationStatus(receiptId)
}
}
pendingReceipts = getPendingReceipts(changedSubmittedContracts)
if (timer.current && pendingReceipts.length === 0) {
clearInterval(timer.current)
timer.current = null
}
setSubmittedContracts((prev) => Object.assign({}, prev, changedSubmittedContracts))
}, 10000)
}
})
return ( return (
<AppContext.Provider value={{ themeType, setThemeType, settings, setSettings, chains, compilationOutput, submittedContracts, setSubmittedContracts }}> <AppContext.Provider value={{ themeType, setThemeType, settings, setSettings, chains, compilationOutput, submittedContracts, setSubmittedContracts }}>
<DisplayRoutes /> <DisplayRoutes />

@ -99,7 +99,7 @@ const ReceiptsBody = ({ contract }: { contract: SubmittedContract }) => {
</thead> </thead>
<tbody> <tbody>
{contract.receipts.map((receipt) => ( {contract.receipts.map((receipt) => (
<tr key={receipt.receiptId}> <tr key={`${contract.id}-${receipt.verifierInfo.name}`}>
<td>{receipt.verifierInfo.name}</td> <td>{receipt.verifierInfo.name}</td>
<td>{receipt.verifierInfo.apiUrl}</td> <td>{receipt.verifierInfo.apiUrl}</td>
<td>{receipt.status}</td> <td>{receipt.status}</td>

@ -15,7 +15,7 @@ const NavItem: React.FC<NavItemProps> = ({ to, icon, title }) => {
className={({ isActive }) => 'p-2 text-decoration-none ' + (isActive ? 'bg-primary text-white' : 'bg-secondary')} className={({ isActive }) => 'p-2 text-decoration-none ' + (isActive ? 'bg-primary text-white' : 'bg-secondary')}
// state={from} // state={from}
> >
<span className="d-flex flex-column align-items-center justify-content-center" style={{ width: '64px' }}> <span className="d-flex flex-column align-items-center justify-content-center" style={{ width: '60px' }}>
<span>{icon}</span> <span>{icon}</span>
<span>{title}</span> <span>{title}</span>
</span> </span>

@ -1,6 +1,7 @@
import { useState } from 'react' import { type Dispatch, type SetStateAction, useState } from 'react'
export function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] { export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>]
{
// State to store our value // State to store our value
// Pass initial state function to useState so logic is only executed once // Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState<T>(() => { const [storedValue, setStoredValue] = useState<T>(() => {
@ -18,12 +19,14 @@ export function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T)
// Return a wrapped version of useState's setter function that ... // Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage. // ... persists the new value to localStorage.
const setValue = (value: T) => { const setValue = (value: SetStateAction<T>) => {
try { try {
// Allow value to be a function so we have same API as useState
const valueToStore = value instanceof Function ? value(storedValue) : value
// Save state // Save state
setStoredValue(value) setStoredValue(valueToStore)
// Save to local storage // Save to local storage
window.localStorage.setItem(key, JSON.stringify(value)) window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) { } catch (error) {
// A more advanced implementation would handle the error case // A more advanced implementation would handle the error case
console.error(error) console.error(error)

@ -28,8 +28,9 @@ export interface VerifierInfo {
export interface VerificationReceipt { export interface VerificationReceipt {
receiptId?: string receiptId?: string
verifierInfo: VerifierInfo verifierInfo: VerifierInfo
status: VerificationStatus | null status: VerificationStatus
message?: string message?: string
contractId: string
} }
export interface SubmittedContract { export interface SubmittedContract {
@ -53,7 +54,9 @@ export interface SubmittedProxyContract {
// This and all nested subtypes should be pure interfaces, so they can be converted to JSON easily // This and all nested subtypes should be pure interfaces, so they can be converted to JSON easily
export interface SubmittedContracts { export interface SubmittedContracts {
[id: string]: SubmittedContract | SubmittedProxyContract // TODO implement Proxy verification
// [id: string]: SubmittedContract | SubmittedProxyContract
[id: string]: SubmittedContract
} }
export function isProxy(contract: SubmittedContract | SubmittedProxyContract): contract is SubmittedProxyContract { export function isProxy(contract: SubmittedContract | SubmittedProxyContract): contract is SubmittedProxyContract {

@ -1,5 +1,5 @@
import type { ChainSettings, ContractVerificationSettings, SettingsForVerifier, VerifierSettings } from './SettingsTypes' import type { ChainSettings, ContractVerificationSettings, SettingsForVerifier, VerifierSettings } from './SettingsTypes'
import { VERIFIERS } from './VerificationTypes' import { VerifierIdentifier, VERIFIERS } from './VerificationTypes'
const DEFAULTS: SettingsForVerifier = { const DEFAULTS: SettingsForVerifier = {
Sourcify: { Sourcify: {
@ -40,3 +40,7 @@ export function mergeChainSettingsWithDefaults(chainId: string, userSettings: Co
} }
return { verifiers } return { verifiers }
} }
export function validConfiguration(chainSettings: ChainSettings | undefined, verifierId: VerifierIdentifier) {
return !!chainSettings && !!chainSettings.verifiers[verifierId]?.apiUrl && (verifierId !== 'Etherscan' || !!chainSettings.verifiers[verifierId]?.apiKey)
}

@ -1,6 +1,6 @@
import { useContext, useState } from 'react' import { useContext, useState } from 'react'
import { SearchableChainDropdown, ContractAddressInput } from '../components' import { SearchableChainDropdown, ContractAddressInput } from '../components'
import { LookupResponse, mergeChainSettingsWithDefaults, VerifierIdentifier, VERIFIERS, type Chain } from '../types' import { LookupResponse, mergeChainSettingsWithDefaults, validConfiguration, VerifierIdentifier, VERIFIERS, type Chain } from '../types'
import { AppContext } from '../AppContext' import { AppContext } from '../AppContext'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import { getVerifier } from '../Verifiers' import { getVerifier } from '../Verifiers'
@ -15,14 +15,17 @@ export const LookupView = () => {
const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined
const handleLookup = () => { const submitDisabled = !!contractAddressError || !contractAddress || !selectedChain
const handleLookup = (e) => {
e.preventDefault()
for (const verifierId of VERIFIERS) { for (const verifierId of VERIFIERS) {
if (!chainSettings.verifiers[verifierId]?.apiUrl || (verifierId === 'Etherscan' && !chainSettings.verifiers[verifierId]?.apiKey)) { if (!validConfiguration(chainSettings, verifierId)) {
continue continue
} }
setLoadingVerifiers((prev) => ({ ...prev, [verifierId]: true })) setLoadingVerifiers((prev) => ({ ...prev, [verifierId]: true }))
console.log(chainSettings.verifiers[verifierId])
const verifier = getVerifier(verifierId, chainSettings.verifiers[verifierId]) const verifier = getVerifier(verifierId, chainSettings.verifiers[verifierId])
verifier verifier
.lookup(contractAddress, selectedChain.chainId.toString()) .lookup(contractAddress, selectedChain.chainId.toString())
@ -44,14 +47,14 @@ export const LookupView = () => {
<ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} /> <ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} />
<button type="submit" className="btn btn-primary" disabled={!!contractAddressError || !contractAddress || !selectedChain}> <button type="submit" className="btn btn-primary" disabled={submitDisabled}>
Lookup Lookup
</button> </button>
</form> </form>
<div className="pt-3"> <div className="pt-3">
{chainSettings && {chainSettings &&
VERIFIERS.map((verifierId) => { VERIFIERS.map((verifierId) => {
if (!chainSettings.verifiers[verifierId]?.apiUrl || (verifierId === 'Etherscan' && !chainSettings.verifiers[verifierId]?.apiKey)) { if (!validConfiguration(chainSettings, verifierId)) {
const tooltipText = 'Configure API in the settings' const tooltipText = 'Configure API in the settings'
return ( return (
<div key={verifierId} className="pt-2"> <div key={verifierId} className="pt-2">

@ -1,14 +1,13 @@
import example from './example.js' import { useContext } from 'react'
import { AccordionReceipt } from '../components/AccordionReceipt' import { AccordionReceipt } from '../components/AccordionReceipt'
import type { SubmittedContracts } from '../types' import { AppContext } from '../AppContext'
export const ReceiptsView = () => { export const ReceiptsView = () => {
const submittedContracts = example as unknown as SubmittedContracts const { submittedContracts } = useContext(AppContext)
// const {submittedContracts} = React.useContext(AppContext);
return ( return (
<div className="accordion" id="receiptsAccordion"> <div className="accordion" id="receiptsAccordion">
{Object.values(submittedContracts).map((contract, index) => ( {Object.values(submittedContracts).reverse().map((contract, index) => (
<AccordionReceipt contract={contract} index={index} /> <AccordionReceipt contract={contract} index={index} />
))} ))}
</div> </div>

@ -2,57 +2,69 @@ import { useContext, useEffect, useState } from 'react'
import { AppContext } from '../AppContext' import { AppContext } from '../AppContext'
import { SearchableChainDropdown, ContractDropdown, ContractAddressInput } from '../components' import { SearchableChainDropdown, ContractDropdown, ContractAddressInput } from '../components'
import type { Chain, SubmittedContract, VerificationReceipt, VerifierInfo } from '../types' import { mergeChainSettingsWithDefaults, type VerifierIdentifier, VERIFIERS, type Chain, type SubmittedContract, type VerificationReceipt, type VerifierInfo, validConfiguration } from '../types'
import { SourcifyVerifier } from '../Verifiers/SourcifyVerifier'
import { EtherscanVerifier } from '../Verifiers/EtherscanVerifier'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { ConstructorArguments } from '../components/ConstructorArguments' import { ConstructorArguments } from '../components/ConstructorArguments'
import { AbstractVerifier, getVerifier } from '../Verifiers'
import { ContractDropdownSelection } from '../components/ContractDropdown' import { ContractDropdownSelection } from '../components/ContractDropdown'
import { CustomTooltip } from '@remix-ui/helper'
import { getVerifier } from '../Verifiers'
export const VerifyView = () => { export const VerifyView = () => {
const { compilationOutput, setSubmittedContracts } = useContext(AppContext) const { compilationOutput, setSubmittedContracts, settings } = useContext(AppContext)
const [contractAddress, setContractAddress] = useState('')
const [selectedChain, setSelectedChain] = useState<Chain | undefined>() const [selectedChain, setSelectedChain] = useState<Chain | undefined>()
const [contractAddress, setContractAddress] = useState('')
const [contractAddressError, setContractAddressError] = useState('')
const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = useState<string>('') const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = useState<string>('')
const [selectedContract, setSelectedContract] = useState<ContractDropdownSelection | undefined>() const [selectedContract, setSelectedContract] = useState<ContractDropdownSelection | undefined>()
const [contractAddressError, setContractAddressError] = useState('') const [enabledVerifiers, setEnabledVerifiers] = useState<Partial<Record<VerifierIdentifier, boolean>>>({})
const navigate = useNavigate() const navigate = useNavigate()
// TODO const chainSettings = selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined
// const sourcifyVerifier = new SourcifyVerifier('http://sourcify.dev/server/', 'Sourcify')
const sourcifyVerifier = new SourcifyVerifier('http://localhost:5555/', 'todo')
const etherscanVerifier = new EtherscanVerifier('https://api.etherscan.io', 'todo', 'API_KEY')
const verifiers = [sourcifyVerifier, etherscanVerifier] // Placeholder, to be derived from settings
const submitDisabled = !!contractAddressError || !contractAddress || !selectedChain || !selectedContract
// Enable all verifiers with valid configuration
useEffect(() => { useEffect(() => {
console.log('Selected chain changed', selectedChain) const changedEnabledVerifiers = {}
for (const verifierId of VERIFIERS) {
if (validConfiguration(chainSettings, verifierId)) {
changedEnabledVerifiers[verifierId] = true
}
}
setEnabledVerifiers(changedEnabledVerifiers)
}, [selectedChain]) }, [selectedChain])
const handleVerifierCheckboxClick = (verifierId: VerifierIdentifier, checked: boolean) => {
setEnabledVerifiers({ ...enabledVerifiers, [verifierId]: checked })
}
const handleVerify = async (e) => { const handleVerify = async (e) => {
e.preventDefault() // Don't change the page e.preventDefault()
console.log('selectedContract', selectedContract)
const { triggerFilePath, filePath, contractName } = selectedContract const { triggerFilePath, filePath, contractName } = selectedContract
// TODO create enabledVerifiers from simple VerifierIdentifier -> boolean mapping
const enabledVerifiers = verifiers.filter((verifier) => verifier.enabled)
const compilerAbstract = compilationOutput[triggerFilePath] const compilerAbstract = compilationOutput[triggerFilePath]
if (!compilerAbstract) { if (!compilerAbstract) {
throw new Error(`Error: Compilation output not found for ${triggerFilePath}`) throw new Error(`Error: Compilation output not found for ${triggerFilePath}`)
} }
const date = new Date() const date = new Date()
// A receipt for each verifier const contractId = selectedChain?.chainId + '-' + contractAddress + '-' + date.toUTCString()
const receipts: VerificationReceipt[] = enabledVerifiers.map((verifier) => { const receipts: VerificationReceipt[] = []
for (const [verifierId, enabled] of Object.entries(enabledVerifiers)) {
if (!enabled) {
continue
}
const verifierInfo: VerifierInfo = { const verifierInfo: VerifierInfo = {
apiUrl: verifier.apiUrl, apiUrl: chainSettings.verifiers[verifierId].apiUrl,
name: verifier instanceof SourcifyVerifier ? 'Sourcify' : 'Etherscan', name: verifierId as VerifierIdentifier,
} }
return { verifierInfo, status: null, receiptId: null, message: null } receipts.push({ verifierInfo, status: 'pending', contractId })
}) }
const newSubmittedContract: SubmittedContract = { const newSubmittedContract: SubmittedContract = {
type: 'contract', type: 'contract',
id: selectedChain?.chainId + '-' + contractAddress + '-' + date.toUTCString(), id: contractId,
address: contractAddress, address: contractAddress,
chainId: selectedChain?.chainId.toString(), chainId: selectedChain?.chainId.toString(),
filePath, filePath,
@ -65,15 +77,14 @@ export const VerifyView = () => {
} }
setSubmittedContracts((prev) => ({ ...prev, [newSubmittedContract.id]: newSubmittedContract })) setSubmittedContracts((prev) => ({ ...prev, [newSubmittedContract.id]: newSubmittedContract }))
console.log('newSubmittedContract:', newSubmittedContract)
// Take user to receipt view // Take user to receipt view
navigate('/receipts') navigate('/receipts')
// Verify for each verifier. forEach does not wait for await and each promise will execute in parallel // Verify for each verifier. forEach does not wait for await and each promise will execute in parallel
receipts.forEach(async (receipt) => { receipts.forEach(async (receipt) => {
const { verifierInfo } = receipt const { verifierInfo } = receipt
const verifier = getVerifier(verifierInfo.name, { apiUrl: verifierInfo.apiUrl, explorerUrl: '' }) const verifierSettings = chainSettings.verifiers[verifierInfo.name]
const verifier = getVerifier(verifierInfo.name, { ...verifierSettings })
try { try {
const response = await verifier.verify(newSubmittedContract, compilerAbstract) const response = await verifier.verify(newSubmittedContract, compilerAbstract)
receipt.status = response.status receipt.status = response.status
@ -91,48 +102,47 @@ export const VerifyView = () => {
}) })
} }
console.log('sourcifyVerifiers:', verifiers)
return ( return (
<form onSubmit={handleVerify}> <form onSubmit={handleVerify}>
<SearchableChainDropdown label="Chain" id="network-dropdown" setSelectedChain={setSelectedChain} selectedChain={selectedChain} /> <SearchableChainDropdown label="Chain" id="network-dropdown" selectedChain={selectedChain} setSelectedChain={setSelectedChain} />
<ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} /> <ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} />
<ContractDropdown label="Contract Name" id="contract-dropdown-1" setSelectedContract={setSelectedContract} /> <ContractDropdown label="Contract Name" id="contract-dropdown-1" setSelectedContract={setSelectedContract} />
<button type="submit" className="btn btn-primary"> {selectedContract && <ConstructorArguments abiEncodedConstructorArgs={abiEncodedConstructorArgs} setAbiEncodedConstructorArgs={setAbiEncodedConstructorArgs} selectedContract={selectedContract} />}
Verify
</button>
<div> <div>
{verifiers?.length > 0 && Verify on:
verifiers.map((verifier) => { {VERIFIERS.map((verifierId) => {
// Temporary fix. The verifier options should be rendered from a constant later.
const name = verifier instanceof SourcifyVerifier ? 'Sourcify' : 'Etherscan'
return ( return (
<div key={name} className="form-check"> <div key={verifierId} className="pt-2 form-check">
<input <input className="form-check-input" type="checkbox" id={`verifier-${verifierId}`} checked={!!enabledVerifiers[verifierId]} onChange={(e) => handleVerifierCheckboxClick(verifierId, e.target.checked)} disabled={!chainSettings || !validConfiguration(chainSettings, verifierId)} />
className="form-check-input"
type="checkbox" <div className="d-flex flex-column align-items-start">
id={`verifier-${name}`} <label htmlFor={`verifier-${verifierId}`} style={{ fontSize: '1rem', lineHeight: '1.5', color: 'var(--text)' }} className={`mb-0 font-weight-bold${!chainSettings || validConfiguration(chainSettings, verifierId) ? '' : ' text-secondary'}`}>
checked={verifier.enabled} {verifierId}
onChange={(e) => {
verifier.enabled = e.target.checked
// Trigger a re-render
// setVerifiers([...verifiers])
}}
/>
<label className="form-check-label" htmlFor={`verifier-${name}`}>
{name} ({verifier.apiUrl})
</label> </label>
{!chainSettings ? (
''
) : validConfiguration(chainSettings, verifierId) ? (
<span className="text-secondary">{chainSettings.verifiers[verifierId].apiUrl}</span>
) : (
<CustomTooltip tooltipText="Configure the API in the settings">
<span className="text-secondary w-auto" style={{ textDecoration: 'underline dotted' }}>
Enable?
</span>
</CustomTooltip>
)}
</div>
</div> </div>
) )
})} })}
</div> </div>
<div>
{/* <ConstructorArguments abiEncodedConstructorArgs={abiEncodedConstructorArgs} setAbiEncodedConstructorArgs={setAbiEncodedConstructorArgs} selectedContract={selectedContract} /> */} <button type="submit" className="btn btn-primary mt-3" disabled={submitDisabled}>
</div> Verify
</button>
</form> </form>
) )
} }

@ -1,104 +0,0 @@
const json = {
'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)': {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
contractName: 'Initializable',
date: '2024-06-20T20:32:36.361Z',
receipts: [
{
verifier: {
name: 'Sourcify Localhost',
apiUrl: 'http://localhost:5555/',
enabled: true,
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
{
verifier: {
name: 'Etherscan',
apiUrl: 'https://api.etherscan.io',
enabled: true,
apiKey: 'API_KEY',
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
],
chainId: '1',
},
'1-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)': {
type: 'proxy',
id: '1-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
implementation: {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
contractName: 'Initializable',
date: '2024-06-20T20:32:36.361Z',
receipts: [
{
verifier: {
name: 'Sourcify Localhost',
apiUrl: 'http://localhost:5555/',
enabled: true,
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
{
verifier: {
name: 'Etherscan',
apiUrl: 'https://api.etherscan.io',
enabled: true,
apiKey: 'API_KEY',
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
],
chainId: '1',
},
proxy: {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
contractName: 'Initializable',
date: '2024-06-20T20:32:36.361Z',
receipts: [
{
verifier: {
name: 'Sourcify Localhost',
apiUrl: 'http://localhost:5555/',
enabled: true,
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
{
verifier: {
name: 'Etherscan',
apiUrl: 'https://api.etherscan.io',
enabled: true,
apiKey: 'API_KEY',
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
],
chainId: '1',
},
},
}
module.exports = json
Loading…
Cancel
Save