diff --git a/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts index 0bfc84d786..ca9f62adfb 100644 --- a/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts @@ -3,6 +3,7 @@ import { SubmittedContract, VerificationResponse } from '../types/VerificationTy export abstract class AbstractVerifier { apiUrl: string + // TODO remove prop enabled: boolean constructor(apiUrl: string) { diff --git a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts index 2a94789001..7ce8a25f70 100644 --- a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts @@ -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 { // 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 { diff --git a/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts index e3ef968d0d..da37ee947a 100644 --- a/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts @@ -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 @@ -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: { diff --git a/apps/contract-verification/src/app/components/ConstructorArguments.tsx b/apps/contract-verification/src/app/components/ConstructorArguments.tsx index f20205cbaa..aab69f2bf5 100644 --- a/apps/contract-verification/src/app/components/ConstructorArguments.tsx +++ b/apps/contract-verification/src/app/components/ConstructorArguments.tsx @@ -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 = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, selectedContract }) => { const { compilationOutput } = React.useContext(AppContext) const [constructorArgsValues, setConstructorArgsValues] = React.useState([]) diff --git a/apps/contract-verification/src/app/types/VerificationTypes.ts b/apps/contract-verification/src/app/types/VerificationTypes.ts index b14942a354..74b250a0f7 100644 --- a/apps/contract-verification/src/app/types/VerificationTypes.ts +++ b/apps/contract-verification/src/app/types/VerificationTypes.ts @@ -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 -} diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 864e538aca..9370594294 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -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