From 831fc88a5d7812a8e2c2fa084bfeb489865cb4a7 Mon Sep 17 00:00:00 2001 From: Manuel Wedler Date: Thu, 4 Jul 2024 18:20:12 +0200 Subject: [PATCH] Use objects as ContractDropdown values --- .../src/app/AppContext.tsx | 4 ---- .../src/app/Verifiers/AbstractVerifier.ts | 4 ++-- .../src/app/Verifiers/EtherscanVerifier.ts | 13 ++++++------- .../src/app/Verifiers/SourcifyVerifier.ts | 19 +++++++++---------- apps/contract-verification/src/app/app.tsx | 10 ++-------- .../app/components/ConstructorArguments.tsx | 14 ++++++++------ .../src/app/components/ContractDropdown.tsx | 19 +++++++++++-------- .../src/app/views/VerifyView.tsx | 18 ++++++++++-------- 8 files changed, 48 insertions(+), 53 deletions(-) diff --git a/apps/contract-verification/src/app/AppContext.tsx b/apps/contract-verification/src/app/AppContext.tsx index eacd43f056..e0def0e176 100644 --- a/apps/contract-verification/src/app/AppContext.tsx +++ b/apps/contract-verification/src/app/AppContext.tsx @@ -10,8 +10,6 @@ type AppContextType = { setThemeType: (themeType: ThemeType) => void chains: Chain[] compilationOutput: { [key: string]: CompilerAbstract } | undefined - selectedContractFileAndName: string | undefined - setSelectedContractFileAndName: React.Dispatch> targetFileName: string | undefined verifiers: AbstractVerifier[] setVerifiers: React.Dispatch> @@ -27,8 +25,6 @@ const defaultContextValue: AppContextType = { }, chains: [], compilationOutput: undefined, - selectedContractFileAndName: undefined, - setSelectedContractFileAndName: (contract: string) => {}, targetFileName: undefined, verifiers: [], setVerifiers: (verifiers: AbstractVerifier[]) => {}, diff --git a/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts index 131c5e29a1..c575cb755b 100644 --- a/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts @@ -1,5 +1,5 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' -import { VerifierIdentifier } from '../types/VerificationTypes' +import { SubmittedContract, VerifierIdentifier } from '../types/VerificationTypes' export abstract class AbstractVerifier { apiUrl: string @@ -10,6 +10,6 @@ export abstract class AbstractVerifier { this.enabled = true } - abstract verify(chainId: string, address: string, compilerAbstract: CompilerAbstract, selectedContractFileAndName: string): Promise + abstract verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract): Promise abstract lookup(): Promise } diff --git a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts index d8dd4a55c6..2a94789001 100644 --- a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts @@ -1,6 +1,6 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' import { AbstractVerifier } from './AbstractVerifier' -import { EtherscanRequest, EtherscanResponse } from '../types/VerificationTypes' +import { EtherscanRequest, EtherscanResponse, SubmittedContract } from '../types/VerificationTypes' export class EtherscanVerifier extends AbstractVerifier { apiKey?: string @@ -10,18 +10,17 @@ export class EtherscanVerifier extends AbstractVerifier { this.apiKey = apiKey } - async verify(chainId: string, address: string, compilerAbstract: CompilerAbstract, selectedContractFileAndName: string, abiEncodedConstructorArgs?: string) { + async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract, abiEncodedConstructorArgs?: string) { const CODE_FORMAT = 'solidity-standard-json-input' - const [_triggerFilePath, selectedFilePath, selectedContractName] = selectedContractFileAndName.split(':') // TODO: Handle version Vyper contracts. This relies on Solidity metadata. - const metadata = JSON.parse(compilerAbstract.data.contracts[selectedFilePath][selectedContractName].metadata) + const metadata = JSON.parse(compilerAbstract.data.contracts[submittedContract.filePath][submittedContract.contractName].metadata) const body: EtherscanRequest = { - chainId, + chainId: submittedContract.chainId, codeformat: CODE_FORMAT, sourceCode: JSON.stringify(compilerAbstract.input), - contractaddress: address, - contractname: selectedContractFileAndName, + contractaddress: submittedContract.address, + contractname: submittedContract.filePath + ':' + submittedContract.contractName, compilerversion: metadata.compiler.version, } diff --git a/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts index 23bdeb39ca..75e6c15e41 100644 --- a/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts @@ -1,19 +1,18 @@ import { CompilerAbstract, SourcesCode } from '@remix-project/remix-solidity' import { AbstractVerifier } from './AbstractVerifier' import { SourcifyReceipt } from '../Receipts/SourcifyReceipt' -import { SourcifyVerificationError, SourcifyVerificationResponse } from '../types/VerificationTypes' +import { SourcifyVerificationError, SourcifyVerificationResponse, SubmittedContract } from '../types/VerificationTypes' export class SourcifyVerifier extends AbstractVerifier { - async verify(chainId: string, address: string, compilerAbstract: CompilerAbstract, selectedContractFileAndName: string) { - const [_triggerFileName, selectedFilePath, selectedContractName] = selectedContractFileAndName.split(':') - const metadataStr = compilerAbstract.data.contracts[selectedFilePath][selectedContractName].metadata + async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract) { + const metadataStr = compilerAbstract.data.contracts[submittedContract.filePath][submittedContract.contractName].metadata const sources = compilerAbstract.source.sources - console.log('selectedFilePath:', selectedFilePath) - console.log('selectedContractName:', selectedContractName) + console.log('selectedFilePath:', submittedContract.filePath) + console.log('selectedContractName:', submittedContract.contractName) console.log('compilerAbstract:', compilerAbstract) console.log('selectedContractMetadataStr:', metadataStr) - console.log('chainId:', chainId) - console.log('address:', address) + console.log('chainId:', submittedContract.chainId) + console.log('address:', submittedContract.address) // from { "filename.sol": {content: "contract MyContract { ... }"} } // to { "filename.sol": "contract MyContract { ... }" } @@ -22,8 +21,8 @@ export class SourcifyVerifier extends AbstractVerifier { return acc }, {}) const body = { - chainId, - address, + chainId: submittedContract.chainId, + address: submittedContract.address, files: { 'metadata.json': metadataStr, ...formattedSources, diff --git a/apps/contract-verification/src/app/app.tsx b/apps/contract-verification/src/app/app.tsx index 4bd677a143..8883950b05 100644 --- a/apps/contract-verification/src/app/app.tsx +++ b/apps/contract-verification/src/app/app.tsx @@ -12,6 +12,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity' import { SourcifyVerifier } from './Verifiers/SourcifyVerifier' import { EtherscanVerifier } from './Verifiers/EtherscanVerifier' import { AbstractVerifier } from './Verifiers/AbstractVerifier' +import { ContractDropdownSelection } from './components/ContractDropdown' const plugin = new ContractVerificationPluginClient() @@ -21,16 +22,9 @@ const App = () => { const [chains, setChains] = useState([]) // State to hold the chains data const [targetFileName, setTargetFileName] = useState('') const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>() - // Contract file and name in format contracts/Storage.sol:contracts/Owner.sol:Owner - // TODO: What happens if contract or filepath contains ":"" ? - const [selectedContractFileAndName, setSelectedContractFileAndName] = useState() const [verifiers, setVerifiers] = useState([]) const [submittedContracts, setSubmittedContracts] = useState({}) - useEffect(() => { - console.log('Selected Contract File And Name Changed', selectedContractFileAndName) - }, [selectedContractFileAndName]) - useEffect(() => { // const sourcifyVerifier = new SourcifyVerifier('http://sourcify.dev/server/', 'Sourcify') const sourcifyVerifier = new SourcifyVerifier('http://localhost:5555/') @@ -67,7 +61,7 @@ const App = () => { }, []) return ( - + ) diff --git a/apps/contract-verification/src/app/components/ConstructorArguments.tsx b/apps/contract-verification/src/app/components/ConstructorArguments.tsx index 22f257e217..f20205cbaa 100644 --- a/apps/contract-verification/src/app/components/ConstructorArguments.tsx +++ b/apps/contract-verification/src/app/components/ConstructorArguments.tsx @@ -2,24 +2,26 @@ import React, { useEffect } from 'react' import { ethers } from 'ethers' import { AppContext } from '../AppContext' +import { ContractDropdownSelection } from './ContractDropdown' const abiCoder = new ethers.utils.AbiCoder() interface ConstructorArgumentsProps { abiEncodedConstructorArgs: string setAbiEncodedConstructorArgs: React.Dispatch> + selectedContract: ContractDropdownSelection } -export const ConstructorArguments: React.FC = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs }) => { - const { selectedContractFileAndName, compilationOutput } = React.useContext(AppContext) +export const ConstructorArguments: React.FC = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, selectedContract }) => { + const { compilationOutput } = React.useContext(AppContext) const [constructorArgsValues, setConstructorArgsValues] = React.useState([]) const [abiEncodingError, setAbiEncodingError] = React.useState('') const [toggleRawInput, setToggleRawInput] = React.useState(false) - const [triggerFilePath, filePath, contractName] = selectedContractFileAndName ? selectedContractFileAndName.split(':') : [] + const { triggerFilePath, filePath, contractName } = selectedContract const selectedCompilerAbstract = triggerFilePath && compilationOutput[triggerFilePath] - const selectedContract = selectedCompilerAbstract?.data?.contracts?.[filePath]?.[contractName] - const abi = selectedContract?.abi + const compiledContract = selectedCompilerAbstract?.data?.contracts?.[filePath]?.[contractName] + const abi = compiledContract?.abi // Wanted to use execution.txHelper.getConstructorInterface from @remix-project/remix-lib but getting errors: 'error getting eth provider options', 'global is not defined' etc. const constructorArgs = abi && abi.find((a) => a.type === 'constructor') && abi.find((a) => a.type === 'constructor').inputs @@ -56,7 +58,7 @@ export const ConstructorArguments: React.FC = ({ abiE } }, [constructorArgsValues, constructorArgs]) - if (!selectedContractFileAndName) return null + if (!selectedContract) return null if (!compilationOutput && Object.keys(compilationOutput).length === 0) return null // No render if no constructor args if (!constructorArgs || constructorArgs.length === 0) return null diff --git a/apps/contract-verification/src/app/components/ContractDropdown.tsx b/apps/contract-verification/src/app/components/ContractDropdown.tsx index d83fc337da..6b62854d61 100644 --- a/apps/contract-verification/src/app/components/ContractDropdown.tsx +++ b/apps/contract-verification/src/app/components/ContractDropdown.tsx @@ -1,19 +1,22 @@ import React, { useEffect, useState, useContext } from 'react' import './ContractDropdown.css' import { AppContext } from '../AppContext' -interface ContractDropdownItem { - value: string - name: string + +export interface ContractDropdownSelection { + triggerFilePath: string + filePath: string + contractName: string } interface ContractDropdownProps { label: string id: string + setSelectedContract: (selection: ContractDropdownSelection) => void } // Chooses one contract from the compilation output. -export const ContractDropdown: React.FC = ({ label, id }) => { - const { setSelectedContractFileAndName, compilationOutput } = useContext(AppContext) +export const ContractDropdown: React.FC = ({ label, id, setSelectedContract }) => { + const { compilationOutput } = useContext(AppContext) useEffect(() => { console.log('CompiilationOutput chainged', compilationOutput) @@ -26,14 +29,14 @@ export const ContractDropdown: React.FC = ({ label, id }) const contractsInFile = contracts[firstFilePath] if (contractsInFile && Object.keys(contractsInFile).length) { const firstContractName = Object.keys(contractsInFile)[0] - setSelectedContractFileAndName(triggerFilePath + ':' + firstFilePath + ':' + firstContractName) + setSelectedContract({ triggerFilePath, filePath: firstFilePath, contractName: firstContractName }) } } }, [compilationOutput]) const handleSelectContract = (event: React.ChangeEvent) => { console.log('selecting ', event.target.value) - setSelectedContractFileAndName(event.target.value) + setSelectedContract(JSON.parse(event.target.value)) } const hasContracts = compilationOutput && Object.keys(compilationOutput).length > 0 @@ -51,7 +54,7 @@ export const ContractDropdown: React.FC = ({ label, id }) [File]: {fileName2} {Object.keys(compilationOutput[compilationTriggerFileName].data.contracts[fileName2]).map((contractName) => ( - ))} diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 5eae6c6208..a635d05ef3 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -8,12 +8,14 @@ import { EtherscanVerifier } from '../Verifiers/EtherscanVerifier' import { useNavigate } from 'react-router-dom' import { ConstructorArguments } from '../components/ConstructorArguments' import { getVerifier } from '../Verifiers' +import { ContractDropdownSelection } from '../components/ContractDropdown' export const VerifyView = () => { - const { compilationOutput, verifiers, setVerifiers, selectedContractFileAndName, setSubmittedContracts } = React.useContext(AppContext) + const { compilationOutput, verifiers, setVerifiers, setSubmittedContracts } = React.useContext(AppContext) const [contractAddress, setContractAddress] = useState('') const [selectedChain, setSelectedChain] = useState() const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = React.useState('') + const [selectedContract, setSelectedContract] = useState() const navigate = useNavigate() useEffect(() => { @@ -23,12 +25,12 @@ export const VerifyView = () => { const handleVerify = async (e) => { e.preventDefault() // Don't change the page - console.log('selectedContractFileAndName', selectedContractFileAndName) - const [triggerFilePath, filePath, contractName] = selectedContractFileAndName.split(':') + console.log('selectedContract', selectedContract) + const { triggerFilePath, filePath, contractName } = selectedContract const enabledVerifiers = verifiers.filter((verifier) => verifier.enabled) const compilerAbstract = compilationOutput[triggerFilePath] if (!compilerAbstract) { - throw new Error(`Error: Compilation output not found for ${selectedContractFileAndName}`) + throw new Error(`Error: Compilation output not found for ${triggerFilePath}`) } const date = new Date() @@ -63,7 +65,7 @@ export const VerifyView = () => { const verifier = getVerifier(verifierInfo.name, { apiUrl: verifierInfo.apiUrl }) if (verifier instanceof SourcifyVerifier) { try { - const response = await verifier.verify(selectedChain?.chainId.toString(), contractAddress, compilerAbstract, selectedContractFileAndName) + const response = await verifier.verify(newSubmittedContract, compilerAbstract) receipt.status = response.result[0].status } catch (e) { const err = e as Error @@ -72,7 +74,7 @@ export const VerifyView = () => { } } else if (verifier instanceof EtherscanVerifier) { try { - const response = await verifier.verify(selectedChain?.chainId.toString(), contractAddress, compilerAbstract, selectedContractFileAndName, abiEncodedConstructorArgs) + const response = await verifier.verify(newSubmittedContract, compilerAbstract, abiEncodedConstructorArgs) receipt.status = 'perfect' } catch (e) { const err = e as Error @@ -94,7 +96,7 @@ export const VerifyView = () => { - +