Decouple SubmittedContract from verifier logic to make it easy to store them in local storage

pull/5285/head
Manuel Wedler 5 months ago committed by Aniket
parent 2a42111e42
commit 2df585c240
  1. 7
      apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts
  2. 7
      apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts
  3. 4
      apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts
  4. 22
      apps/contract-verification/src/app/Verifiers/index.ts
  5. 10
      apps/contract-verification/src/app/app.tsx
  6. 6
      apps/contract-verification/src/app/components/AccordionReceipt.tsx
  7. 27
      apps/contract-verification/src/app/types/VerificationTypes.ts
  8. 35
      apps/contract-verification/src/app/views/VerifyView.tsx

@ -1,15 +1,12 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { SourcifyReceipt } from '../Receipts/SourcifyReceipt'
import { EtherscanReceipt } from '../Receipts/EtherscanReceipt'
import { VerifierIdentifier } from '../types/VerificationTypes'
export abstract class AbstractVerifier {
name: string
apiUrl: string
enabled: boolean
constructor(apiUrl: string, name: string) {
constructor(apiUrl: string) {
this.apiUrl = apiUrl
this.name = name
this.enabled = true
}

@ -1,13 +1,12 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { AbstractVerifier } from './AbstractVerifier'
import { EtherscanReceipt } from '../Receipts/EtherscanReceipt'
import { EtherscanRequest, EtherscanResponse } from '../types/VerificationTypes'
export class EtherscanVerifier extends AbstractVerifier {
apiKey: string
apiKey?: string
constructor(apiUrl: string, name: string = 'Etherscan', apiKey: string) {
super(apiUrl, name)
constructor(apiUrl: string, apiKey?: string) {
super(apiUrl)
this.apiKey = apiKey
}

@ -4,10 +4,6 @@ import { SourcifyReceipt } from '../Receipts/SourcifyReceipt'
import { SourcifyVerificationError, SourcifyVerificationResponse } from '../types/VerificationTypes'
export class SourcifyVerifier extends AbstractVerifier {
constructor(apiUrl: string, name: string = 'Sourcify') {
super(apiUrl, name)
}
async verify(chainId: string, address: string, compilerAbstract: CompilerAbstract, selectedContractFileAndName: string) {
const [_triggerFileName, selectedFilePath, selectedContractName] = selectedContractFileAndName.split(':')
const metadataStr = compilerAbstract.data.contracts[selectedFilePath][selectedContractName].metadata

@ -0,0 +1,22 @@
import { VerifierIdentifier, VerifierSettings } from '../types/VerificationTypes'
import { AbstractVerifier } from './AbstractVerifier'
import { EtherscanVerifier } from './EtherscanVerifier'
import { SourcifyVerifier } from './SourcifyVerifier'
export { AbstractVerifier } from './AbstractVerifier'
export { SourcifyVerifier } from './SourcifyVerifier'
export { EtherscanVerifier } from './EtherscanVerifier'
export function getVerifier(identifier: VerifierIdentifier, settings: VerifierSettings): AbstractVerifier {
switch (identifier) {
case 'Sourcify':
return new SourcifyVerifier(settings.apiUrl)
case 'Etherscan':
if (!settings.apiKey) {
throw new Error('The Etherscan verifier requires an API key.')
}
return new EtherscanVerifier(settings.apiUrl, settings.apiKey)
case 'Blockscout':
return new EtherscanVerifier(settings.apiUrl)
}
}

@ -33,8 +33,8 @@ const App = () => {
useEffect(() => {
// const sourcifyVerifier = new SourcifyVerifier('http://sourcify.dev/server/', 'Sourcify')
const sourcifyVerifier = new SourcifyVerifier('http://localhost:5555/', 'Sourcify Localhost')
const etherscanVerifier = new EtherscanVerifier('https://api.etherscan.io', 'Etherscan', 'API_KEY')
const sourcifyVerifier = new SourcifyVerifier('http://localhost:5555/')
const etherscanVerifier = new EtherscanVerifier('https://api.etherscan.io', 'API_KEY')
setVerifiers([sourcifyVerifier, etherscanVerifier])
// 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
@ -46,10 +46,10 @@ const App = () => {
})
// Subscribe to compilations
plugin.on('compilerArtefacts' as any, 'compilationSaved', (compilerAbstract: { [key: string]: CompilerAbstract }) => {
plugin.on('compilerArtefacts' as any, 'compilationSaved', (compilerAbstracts: { [key: string]: CompilerAbstract }) => {
console.log('compilerArtefacts.compilationSaved')
console.log(compilerAbstract)
setCompilationOutput((prev) => ({ ...(prev || {}), ...compilerAbstract }))
console.log(compilerAbstracts)
setCompilationOutput((prev) => ({ ...(prev || {}), ...compilerAbstracts }))
})
// TODO: Is there a way to get all compilations from the `build-info` files without having to compile again?

@ -84,7 +84,7 @@ const ReceiptsBody = ({ contract }: { contract: SubmittedContract }) => {
</div>
<div>
<span className="font-weight-bold">Submission: </span>
{contract.date.toLocaleString()}
{new Date(contract.date).toLocaleString()}
</div>
<div className="table-responsive">
<table className="table">
@ -100,8 +100,8 @@ const ReceiptsBody = ({ contract }: { contract: SubmittedContract }) => {
<tbody>
{contract.receipts.map((receipt) => (
<tr key={receipt.receiptId}>
<td>{receipt.verifier.name}</td>
<td>{receipt.verifier.apiUrl}</td>
<td>{receipt.verifierInfo.name}</td>
<td>{receipt.verifierInfo.apiUrl}</td>
<td>{receipt.status}</td>
<td>{receipt.message}</td>
<td>{receipt.receiptId}</td>

@ -1,10 +1,3 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { AbstractVerifier } from '../Verifiers/AbstractVerifier'
import { SourcifyVerifier } from '../Verifiers/SourcifyVerifier'
import { EtherscanVerifier } from '../Verifiers/EtherscanVerifier'
export type SourcifyVerificationStatus = 'perfect' | 'partial' | null
interface Currency {
name: string
symbol: string
@ -24,9 +17,21 @@ export interface Chain {
infoURL?: string
}
export type VerifierIdentifier = "Sourcify" | "Etherscan" | "Blockscout"
export interface VerifierSettings {
apiUrl: string
apiKey?: string
}
export interface VerifierInfo {
name: VerifierIdentifier
apiUrl: string
}
export interface VerificationReceipt {
receiptId?: string
verifier: AbstractVerifier
verifierInfo: VerifierInfo
status: SourcifyVerificationStatus | 'error' | null
message?: string
}
@ -38,8 +43,7 @@ export interface SubmittedContract {
contractName: string
chainId: string
address: string
compilerAbstract: CompilerAbstract
date: Date
date: string
receipts: VerificationReceipt[]
}
@ -50,6 +54,7 @@ export interface SubmittedProxyContract {
proxy: SubmittedContract
}
// This and all nested subtypes should be pure interfaces, so they can be converted to JSON easily
export interface SubmittedContracts {
[id: string]: SubmittedContract | SubmittedProxyContract
}
@ -62,6 +67,8 @@ export function isContract(contract: SubmittedContract | SubmittedProxyContract)
return contract.type === 'contract'
}
export type SourcifyVerificationStatus = 'perfect' | 'partial' | null
export interface SourcifyVerificationResponse {
result: [
{

@ -2,11 +2,12 @@ import React, { useEffect, useState } from 'react'
import { AppContext } from '../AppContext'
import { SearchableChainDropdown, ContractDropdown, ContractAddressInput } from '../components'
import { Chain, SubmittedContract, VerificationReceipt } from '../types/VerificationTypes'
import { Chain, SubmittedContract, VerificationReceipt, VerifierInfo } from '../types/VerificationTypes'
import { SourcifyVerifier } from '../Verifiers/SourcifyVerifier'
import { EtherscanVerifier } from '../Verifiers/EtherscanVerifier'
import { useNavigate } from 'react-router-dom'
import { ConstructorArguments } from '../components/ConstructorArguments'
import { getVerifier } from '../Verifiers'
export const VerifyView = () => {
const { compilationOutput, verifiers, setVerifiers, selectedContractFileAndName, setSubmittedContracts } = React.useContext(AppContext)
@ -32,16 +33,21 @@ export const VerifyView = () => {
const date = new Date()
// A receipt for each verifier
const receipts: VerificationReceipt[] = enabledVerifiers.map((verifier) => ({ verifier, status: null, receiptId: null, message: null }))
const receipts: VerificationReceipt[] = enabledVerifiers.map((verifier) => {
const verifierInfo: VerifierInfo = {
apiUrl: verifier.apiUrl,
name: verifier instanceof SourcifyVerifier ? 'Sourcify' : 'Etherscan',
}
return { verifierInfo, status: null, receiptId: null, message: null }
})
const newSubmittedContract: SubmittedContract = {
type: 'contract',
id: selectedChain?.chainId + '-' + contractAddress + '-' + date.toString(),
id: selectedChain?.chainId + '-' + contractAddress + '-' + date.toUTCString(),
address: contractAddress,
chainId: selectedChain?.chainId.toString(),
filePath,
contractName,
compilerAbstract,
date,
date: date.toUTCString(),
receipts,
}
setSubmittedContracts((prev) => ({ ...prev, [newSubmittedContract.id]: newSubmittedContract }))
@ -53,7 +59,8 @@ export const VerifyView = () => {
// Verify for each verifier. forEach does not wait for await and each promise will execute in parallel
receipts.forEach(async (receipt) => {
const { verifier } = receipt
const { verifierInfo } = receipt
const verifier = getVerifier(verifierInfo.name, { apiUrl: verifierInfo.apiUrl })
if (verifier instanceof SourcifyVerifier) {
try {
const response = await verifier.verify(selectedChain?.chainId.toString(), contractAddress, compilerAbstract, selectedContractFileAndName)
@ -95,12 +102,15 @@ export const VerifyView = () => {
<div>
{verifiers?.length > 0 &&
verifiers.map((verifier) => (
<div key={verifier.name} className="form-check">
verifiers.map((verifier) => {
// Temporary fix. The verifier options should be rendered from a constant later.
const name = verifier instanceof SourcifyVerifier ? 'Sourcify' : 'Etherscan'
return (
<div key={name} className="form-check">
<input
className="form-check-input"
type="checkbox"
id={`verifier-${verifier.name}`}
id={`verifier-${name}`}
checked={verifier.enabled}
onChange={(e) => {
verifier.enabled = e.target.checked
@ -108,11 +118,12 @@ export const VerifyView = () => {
setVerifiers([...verifiers])
}}
/>
<label className="form-check-label" htmlFor={`verifier-${verifier.name}`}>
{verifier.name} ({verifier.apiUrl})
<label className="form-check-label" htmlFor={`verifier-${name}`}>
{name} ({verifier.apiUrl})
</label>
</div>
))}
)
})}
</div>
<div>
<ConstructorArguments abiEncodedConstructorArgs={abiEncodedConstructorArgs} setAbiEncodedConstructorArgs={setAbiEncodedConstructorArgs} />

Loading…
Cancel
Save