Improve EtherscanVerifier

pull/5285/head
Manuel Wedler 5 months ago committed by Aniket
parent 14d10bf21d
commit cfbf7e55a0
  1. 1
      apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts
  2. 50
      apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts
  3. 4
      apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts
  4. 4
      apps/contract-verification/src/app/components/ConstructorArguments.tsx
  5. 16
      apps/contract-verification/src/app/types/VerificationTypes.ts
  6. 34
      apps/contract-verification/src/app/views/VerifyView.tsx

@ -3,6 +3,7 @@ import { SubmittedContract, VerificationResponse } from '../types/VerificationTy
export abstract class AbstractVerifier {
apiUrl: string
// TODO remove prop
enabled: boolean
constructor(apiUrl: string) {

@ -1,6 +1,22 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { AbstractVerifier } from './AbstractVerifier'
import { EtherscanRequest, EtherscanResponse, SubmittedContract } from '../types/VerificationTypes'
import { SubmittedContract, VerificationResponse } from '../types/VerificationTypes'
interface EtherscanVerificationRequest {
chainId?: string
codeformat: 'solidity-single-file' | 'solidity-standard-json-input'
sourceCode: string
contractaddress: string
contractname: string
compilerversion: string
constructorArguements?: string
}
interface EtherscanVerificationResponse {
status: '0' | '1'
message: string
result: string
}
export class EtherscanVerifier extends AbstractVerifier {
apiKey?: string
@ -10,28 +26,28 @@ export class EtherscanVerifier extends AbstractVerifier {
this.apiKey = apiKey
}
async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract, abiEncodedConstructorArgs?: string) {
const CODE_FORMAT = 'solidity-standard-json-input'
async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract): Promise<VerificationResponse> {
// TODO: Handle version Vyper contracts. This relies on Solidity metadata.
const metadata = JSON.parse(compilerAbstract.data.contracts[submittedContract.filePath][submittedContract.contractName].metadata)
const body: EtherscanRequest = {
const body: EtherscanVerificationRequest = {
chainId: submittedContract.chainId,
codeformat: CODE_FORMAT,
codeformat: 'solidity-standard-json-input',
sourceCode: JSON.stringify(compilerAbstract.input),
contractaddress: submittedContract.address,
contractname: submittedContract.filePath + ':' + submittedContract.contractName,
compilerversion: metadata.compiler.version,
}
if (abiEncodedConstructorArgs) {
body.constructorArguements = abiEncodedConstructorArgs
if (submittedContract.abiEncodedConstructorArgs) {
body.constructorArguements = submittedContract.abiEncodedConstructorArgs
}
const url = new URL('api', this.apiUrl)
url.searchParams.append('module', 'contract')
url.searchParams.append('action', 'verifysourcecode')
url.searchParams.append('apikey', this.apiKey)
if (this.apiKey) {
url.searchParams.append('apikey', this.apiKey)
}
const response = await fetch(url.href, {
method: 'POST',
@ -42,17 +58,19 @@ export class EtherscanVerifier extends AbstractVerifier {
})
if (!response.ok) {
throw new Error(`Request error Status:${response.status} Response: ${await response.text()}`)
const responseText = await response.text()
console.error('Error on Etherscan API verification at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + (responseText))
throw new Error(responseText)
}
const data: EtherscanResponse = await response.json()
console.log(data)
if (data.status !== '1' || data.message !== 'OK') {
console.error(`Error on Etherscan verification at ${this.apiUrl}: ${data.result}`)
throw new Error(data.result)
const verificationResponse: EtherscanVerificationResponse = await response.json()
if (verificationResponse.status !== '1' || verificationResponse.message !== 'OK') {
console.error('Error on Etherscan API verification at ' + this.apiUrl + '\nStatus: ' + verificationResponse.status + '\nMessage: ' + verificationResponse.message + '\nResult: ' + verificationResponse.result)
throw new Error(verificationResponse.result)
}
return data
return { status: 'pending', receiptId: verificationResponse.result }
}
async lookup(): Promise<any> {

@ -2,7 +2,7 @@ import { CompilerAbstract, SourcesCode } from '@remix-project/remix-solidity'
import { AbstractVerifier } from './AbstractVerifier'
import { SubmittedContract, VerificationResponse } from '../types/VerificationTypes'
interface SourcifyVerifyRequest {
interface SourcifyVerificationRequest {
address: string
chain: string
files: Record<string, string>
@ -47,7 +47,7 @@ export class SourcifyVerifier extends AbstractVerifier {
acc[fileName] = content
return acc
}, {})
const body: SourcifyVerifyRequest = {
const body: SourcifyVerificationRequest = {
chain: submittedContract.chainId,
address: submittedContract.address,
files: {

@ -12,6 +12,10 @@ interface ConstructorArgumentsProps {
selectedContract: ContractDropdownSelection
}
// TODO
// Add mapping VerifierIdentifier -> ConstructorArgsRequired
// Check enabledVerifiers: when not required, don't show component and set to null
export const ConstructorArguments: React.FC<ConstructorArgumentsProps> = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, selectedContract }) => {
const { compilationOutput } = React.useContext(AppContext)
const [constructorArgsValues, setConstructorArgsValues] = React.useState<string[]>([])

@ -43,6 +43,7 @@ export interface SubmittedContract {
contractName: string
chainId: string
address: string
abiEncodedConstructorArgs?: string
date: string
receipts: VerificationReceipt[]
}
@ -71,18 +72,3 @@ export interface VerificationResponse {
status: string | 'pending'
receiptId: string | null
}
export interface EtherscanRequest {
chainId?: string
codeformat: 'solidity-standard-json-input'
sourceCode: string
contractaddress: string
contractname: string
compilerversion: string
constructorArguements?: string
}
export interface EtherscanResponse {
status: '0' | '1'
message: string
result: string
}

@ -27,6 +27,7 @@ export const VerifyView = () => {
console.log('selectedContract', 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]
if (!compilerAbstract) {
@ -52,6 +53,9 @@ export const VerifyView = () => {
date: date.toUTCString(),
receipts,
}
if (abiEncodedConstructorArgs) {
newSubmittedContract.abiEncodedConstructorArgs = abiEncodedConstructorArgs
}
setSubmittedContracts((prev) => ({ ...prev, [newSubmittedContract.id]: newSubmittedContract }))
console.log('newSubmittedContract:', newSubmittedContract)
@ -63,28 +67,16 @@ export const VerifyView = () => {
receipts.forEach(async (receipt) => {
const { verifierInfo } = receipt
const verifier = getVerifier(verifierInfo.name, { apiUrl: verifierInfo.apiUrl })
if (verifier instanceof SourcifyVerifier) {
try {
const response = await verifier.verify(newSubmittedContract, compilerAbstract)
receipt.status = response.status
if (response.receiptId) {
receipt.receiptId = response.receiptId
}
} catch (e) {
const err = e as Error
receipt.status = 'error'
receipt.message = err.message
}
} else if (verifier instanceof EtherscanVerifier) {
try {
// TODO generalize parameters to pass constructorargs optionally on the AbstractVerifier
const response = await verifier.verify(newSubmittedContract, compilerAbstract, abiEncodedConstructorArgs)
receipt.status = 'perfect'
} catch (e) {
const err = e as Error
receipt.status = 'error'
receipt.message = err.message
try {
const response = await verifier.verify(newSubmittedContract, compilerAbstract)
receipt.status = response.status
if (response.receiptId) {
receipt.receiptId = response.receiptId
}
} catch (e) {
const err = e as Error
receipt.status = 'error'
receipt.message = err.message
}
// Update the UI

Loading…
Cancel
Save