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' import type { LookupResponse, SubmittedContract, VerificationResponse, VerificationStatus } from '../types'
export interface AbstractVerifier { export interface AbstractVerifier {
checkVerificationStatus?(receiptId: string): Promise<VerificationStatus> checkVerificationStatus?(receiptId: string): Promise<VerificationResponse>
} }
export abstract class AbstractVerifier { export abstract class AbstractVerifier {

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

@ -60,7 +60,7 @@ const App = () => {
// Check statuses of receipts // Check statuses of receipts
for (const submission of Object.values(submissions)) { for (const submission of Object.values(submissions)) {
for (const receipt of submission.receipts) { for (const receipt of submission.receipts) {
if (receipt.status === 'pending' && receipt.verifierInfo.name !== 'Sourcify') { if (receipt.status === 'pending') {
pendingReceipts.push(receipt) pendingReceipts.push(receipt)
} }
} }
@ -75,7 +75,8 @@ const App = () => {
clearInterval(timer.current) clearInterval(timer.current)
timer.current = null timer.current = null
} }
timer.current = setInterval(async () => {
const pollStatus = async () => {
const changedSubmittedContracts = { ...submittedContracts } const changedSubmittedContracts = { ...submittedContracts }
for (const receipt of pendingReceipts) { 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 // In case the user overwrites the API later, prefer the one stored in localStorage
const verifier = getVerifier(verifierInfo.name, { ...verifierSettings, apiUrl: verifierInfo.apiUrl }) 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 timer.current = null
} }
setSubmittedContracts((prev) => Object.assign({}, prev, changedSubmittedContracts)) 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 { export interface VerificationResponse {
status: VerificationStatus status: VerificationStatus
receiptId: string | null receiptId: string | null
message?: string
} }
export interface LookupResponse { export interface LookupResponse {

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

Loading…
Cancel
Save