diff --git a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts index 22237ec0ec..eda856b595 100644 --- a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts +++ b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts @@ -39,7 +39,7 @@ export class EtherscanVerifier extends AbstractVerifier { formData.append('contractaddress', submittedContract.address) formData.append('contractname', submittedContract.filePath + ':' + submittedContract.contractName) formData.append('compilerversion', `v${metadata.compiler.version}`) - formData.append('constructorArguements', submittedContract.abiEncodedConstructorArgs ?? '') + formData.append('constructorArguements', submittedContract.abiEncodedConstructorArgs.replace('0x', '') ?? '') const url = new URL(this.apiUrl + '/api') url.searchParams.append('module', 'contract') diff --git a/apps/contract-verification/src/app/components/ConstructorArguments.tsx b/apps/contract-verification/src/app/components/ConstructorArguments.tsx index aab69f2bf5..2fe7d8d971 100644 --- a/apps/contract-verification/src/app/components/ConstructorArguments.tsx +++ b/apps/contract-verification/src/app/components/ConstructorArguments.tsx @@ -1,84 +1,90 @@ -import React, { useEffect } from 'react' +import { useContext, useEffect, useState } 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> + abiEncodingError: string + setAbiEncodingError: React.Dispatch> 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([]) - const [abiEncodingError, setAbiEncodingError] = React.useState('') - const [toggleRawInput, setToggleRawInput] = React.useState(false) +export const ConstructorArguments: React.FC = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, abiEncodingError, setAbiEncodingError, selectedContract }) => { + const { compilationOutput } = useContext(AppContext) + const [toggleRawInput, setToggleRawInput] = useState(false) const { triggerFilePath, filePath, contractName } = selectedContract const selectedCompilerAbstract = triggerFilePath && compilationOutput[triggerFilePath] 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 + const constructorArgs = abi && abi.find((a) => a.type === 'constructor')?.inputs + const [constructorArgsValues, setConstructorArgsValues] = useState(Array(constructorArgs?.length ?? 0).fill('')) useEffect(() => { - if (!constructorArgs) { - setConstructorArgsValues([]) - return + setConstructorArgsValues([]) + setAbiEncodedConstructorArgs('') + setAbiEncodingError('') + if (constructorArgs) { + setConstructorArgsValues(Array(constructorArgs.length).fill('')) } - setConstructorArgsValues(Array(constructorArgs.length).fill('')) }, [constructorArgs]) - // Do the abi encoding when user values are input - useEffect(() => { - if (!constructorArgs) { + const handleConstructorArgs = (value: string, index: number) => { + const changedConstructorArgsValues = [...constructorArgsValues.slice(0, index), value, ...constructorArgsValues.slice(index + 1)] + setConstructorArgsValues(changedConstructorArgsValues) + + // if any constructorArgsValue is falsey (empty etc.), don't encode yet + if (changedConstructorArgsValues.some((value) => !value)) { setAbiEncodedConstructorArgs('') setAbiEncodingError('') return } - if (constructorArgsValues.length !== constructorArgs.length) return - // if any constructorArgsValue is falsey (empty etc.), don't encode yet - if (constructorArgsValues.some((value) => !value)) return setAbiEncodingError('') const types = constructorArgs.map((inp) => inp.type) + const parsedArgsValues = [] + for (const arg of changedConstructorArgsValues) { + try { + parsedArgsValues.push(JSON.parse(arg)) + } catch (e) { + parsedArgsValues.push(arg) + } + } + try { - console.log('constructorArgsValues', constructorArgsValues) - console.log('types', types) - const newAbiEncoding = abiCoder.encode(types, constructorArgsValues) + const newAbiEncoding = ethers.utils.defaultAbiCoder.encode(types, parsedArgsValues) setAbiEncodedConstructorArgs(newAbiEncoding) setAbiEncodingError('') } catch (e) { console.error(e) + setAbiEncodedConstructorArgs('') setAbiEncodingError('Encoding error: ' + e.message) } - }, [constructorArgsValues, constructorArgs]) - - 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 + } const handleRawConstructorArgs = (value: string) => { + setAbiEncodedConstructorArgs(value) try { - const decoded = abiCoder.decode( + const decoded = ethers.utils.defaultAbiCoder.decode( constructorArgs.map((inp) => inp.type), value ) - setConstructorArgsValues(decoded.map((val) => val.toString())) + setConstructorArgsValues(decoded.map((val) => JSON.stringify(val))) + setAbiEncodingError('') } catch (e) { console.error(e) setAbiEncodingError('Decoding error: ' + e.message) } } + + 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 + return (
@@ -99,7 +105,7 @@ export const ConstructorArguments: React.FC = ({ abiE {constructorArgs.map((inp, i) => (
{inp.name}
- setConstructorArgsValues([...constructorArgsValues.slice(0, i), e.target.value, ...constructorArgsValues.slice(i + 1)])} /> + handleConstructorArgs(e.target.value, i)} />
))} {abiEncodedConstructorArgs && ( diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 4a91f46762..a2c6f1667b 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -17,6 +17,7 @@ export const VerifyView = () => { const [contractAddress, setContractAddress] = useState('') const [contractAddressError, setContractAddressError] = useState('') const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = useState('') + const [abiEncodingError, setAbiEncodingError] = useState('') const [selectedContract, setSelectedContract] = useState() const [enabledVerifiers, setEnabledVerifiers] = useState>>({}) const navigate = useNavigate() @@ -113,7 +114,7 @@ export const VerifyView = () => { - {selectedContract && } + {selectedContract && }
Verify on: