Fix Etherscan API verification

pull/5285/head
Manuel Wedler 4 months ago committed by Aniket
parent b94c57ac74
commit 377b94e1ce
  1. 2
      apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts
  2. 57
      apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts
  3. 20
      apps/contract-verification/src/app/app.tsx
  4. 1
      apps/contract-verification/src/app/types/VerificationTypes.ts
  5. 9
      apps/contract-verification/src/app/views/VerifyView.tsx

@ -2,7 +2,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
import type { LookupResponse, SubmittedContract, VerificationResponse, VerificationStatus } from '../types'
export interface AbstractVerifier {
checkVerificationStatus?(receiptId: string): Promise<VerificationStatus>
checkVerificationStatus?(receiptId: string): Promise<VerificationResponse>
}
export abstract class AbstractVerifier {

@ -21,7 +21,7 @@ interface EtherscanRpcResponse {
interface EtherscanCheckStatusResponse {
status: '0' | '1'
message: string
result: 'Pending in queue' | 'Pass - Verified' | 'Fail - Unable to verify' | 'Unknown UID'
result: 'Pending in queue' | 'Pass - Verified' | 'Fail - Unable to verify' | 'Already Verified' | 'Unknown UID'
}
export class EtherscanVerifier extends AbstractVerifier {
@ -32,18 +32,14 @@ export class EtherscanVerifier extends AbstractVerifier {
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: EtherscanVerificationRequest = {
chainId: submittedContract.chainId,
codeformat: 'solidity-standard-json-input',
sourceCode: JSON.stringify(compilerAbstract.input),
contractaddress: submittedContract.address,
contractname: submittedContract.filePath + ':' + submittedContract.contractName,
compilerversion: metadata.compiler.version,
}
if (submittedContract.abiEncodedConstructorArgs) {
body.constructorArguements = submittedContract.abiEncodedConstructorArgs
}
const formData = new FormData()
formData.append('chainId', submittedContract.chainId)
formData.append('codeformat', 'solidity-standard-json-input')
formData.append('sourceCode', compilerAbstract.input.toString())
formData.append('contractaddress', submittedContract.address)
formData.append('contractname', submittedContract.filePath + ':' + submittedContract.contractName)
formData.append('compilerversion', `v${metadata.compiler.version}`)
formData.append('constructorArguements', submittedContract.abiEncodedConstructorArgs ?? '')
const url = new URL(this.apiUrl + '/api')
url.searchParams.append('module', 'contract')
@ -54,10 +50,7 @@ export class EtherscanVerifier extends AbstractVerifier {
const response = await fetch(url.href, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
body: formData,
})
if (!response.ok) {
@ -76,7 +69,7 @@ export class EtherscanVerifier extends AbstractVerifier {
return { status: 'pending', receiptId: verificationResponse.result }
}
async checkVerificationStatus(receiptId: string): Promise<VerificationStatus> {
async checkVerificationStatus(receiptId: string): Promise<VerificationResponse> {
const url = new URL(this.apiUrl + '/api')
url.searchParams.append('module', 'contract')
url.searchParams.append('action', 'checkverifystatus')
@ -95,28 +88,26 @@ export class EtherscanVerifier extends AbstractVerifier {
const checkStatusResponse: EtherscanCheckStatusResponse = await response.json()
if (checkStatusResponse.status !== '1' || !checkStatusResponse.message.startsWith('OK')) {
console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result)
throw new Error(checkStatusResponse.result)
if (checkStatusResponse.result === 'Fail - Unable to verify') {
return { status: 'failed', receiptId, message: checkStatusResponse.result }
}
if (checkStatusResponse.result === 'Pending in queue') {
return { status: 'pending', receiptId }
}
if (checkStatusResponse.result === 'Pass - Verified' || checkStatusResponse.result === 'Already Verified') {
return { status: 'verified', receiptId }
}
if (checkStatusResponse.result === 'Unknown UID') {
console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result)
throw new Error(checkStatusResponse.result)
return { status: 'failed', receiptId, message: checkStatusResponse.result }
}
let status: VerificationStatus = 'unknown'
if (checkStatusResponse.result === 'Fail - Unable to verify') {
status = 'failed'
}
if (checkStatusResponse.result === 'Pending in queue') {
status = 'pending'
}
if (checkStatusResponse.result === 'Pass - Verified') {
status = 'verified'
if (checkStatusResponse.status !== '1' || !checkStatusResponse.message.startsWith('OK')) {
console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result)
throw new Error(checkStatusResponse.result)
}
return status
return { status: 'unknown', receiptId }
}
async lookup(contractAddress: string, chainId: string): Promise<LookupResponse> {

@ -60,7 +60,7 @@ const App = () => {
// 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') {
if (receipt.status === 'pending') {
pendingReceipts.push(receipt)
}
}
@ -75,7 +75,8 @@ const App = () => {
clearInterval(timer.current)
timer.current = null
}
timer.current = setInterval(async () => {
const pollStatus = async () => {
const changedSubmittedContracts = { ...submittedContracts }
for (const receipt of pendingReceipts) {
@ -89,7 +90,15 @@ const App = () => {
// 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)
if (!verifier.checkVerificationStatus) {
continue
}
try {
const { status, message } = await verifier.checkVerificationStatus(receiptId)
receipt.status = status
receipt.message = message
} catch (e) {} // try again in next call
}
}
@ -99,7 +108,10 @@ const App = () => {
timer.current = null
}
setSubmittedContracts((prev) => Object.assign({}, prev, changedSubmittedContracts))
}, 10000)
}
pollStatus()
timer.current = setInterval(pollStatus, 10000)
}
})

@ -74,6 +74,7 @@ export type VerificationStatus = SourcifyStatus | EtherscanStatus | 'failed' | '
export interface VerificationResponse {
status: VerificationStatus
receiptId: string | null
message?: string
}
export interface LookupResponse {

@ -86,10 +86,11 @@ export const VerifyView = () => {
const verifierSettings = chainSettings.verifiers[verifierInfo.name]
const verifier = getVerifier(verifierInfo.name, { ...verifierSettings })
try {
const response = await verifier.verify(newSubmittedContract, compilerAbstract)
receipt.status = response.status
if (response.receiptId) {
receipt.receiptId = response.receiptId
const { status, message, receiptId } = await verifier.verify(newSubmittedContract, compilerAbstract)
receipt.status = status
receipt.message = message
if (receiptId) {
receipt.receiptId = receiptId
}
} catch (e) {
const err = e as Error

Loading…
Cancel
Save