Commit 1374ea80 authored by tom's avatar tom

Update verification methods in the UI

parent 07c2433f
import type { SmartContractVerificationMethodExtra } from 'types/client/contract';
import { SMART_CONTRACT_EXTRA_VERIFICATION_METHODS } from 'types/client/contract';
import type { AddressViewId, IdenticonType } from 'types/views/address'; import type { AddressViewId, IdenticonType } from 'types/views/address';
import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'types/views/address'; import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'types/views/address';
...@@ -24,10 +26,26 @@ const hiddenViews = (() => { ...@@ -24,10 +26,26 @@ const hiddenViews = (() => {
return result; return result;
})(); })();
const extraVerificationMethods: Array<SmartContractVerificationMethodExtra> = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS');
if (envValue === 'none') {
return [];
}
if (!envValue) {
return SMART_CONTRACT_EXTRA_VERIFICATION_METHODS;
}
const parsedMethods = parseEnvJson<Array<SmartContractVerificationMethodExtra>>(getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS')) || [];
return SMART_CONTRACT_EXTRA_VERIFICATION_METHODS.filter((method) => parsedMethods.includes(method));
})();
const config = Object.freeze({ const config = Object.freeze({
identiconType, identiconType,
hiddenViews, hiddenViews,
solidityscanEnabled: getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED') === 'true', solidityscanEnabled: getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED') === 'true',
extraVerificationMethods,
}); });
export default config; export default config;
/* eslint-disable max-len */
declare module 'yup' { declare module 'yup' {
interface StringSchema { interface StringSchema {
// Yup's URL validator is not perfect so we made our own // Yup's URL validator is not perfect so we made our own
...@@ -11,7 +12,7 @@ import * as yup from 'yup'; ...@@ -11,7 +12,7 @@ import * as yup from 'yup';
import type { AdButlerConfig } from '../../../types/client/adButlerConfig'; import type { AdButlerConfig } from '../../../types/client/adButlerConfig';
import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS } from '../../../types/client/adProviders'; import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS } from '../../../types/client/adProviders';
import type { AdTextProviders, AdBannerProviders, AdBannerAdditionalProviders } from '../../../types/client/adProviders'; import type { AdTextProviders, AdBannerProviders, AdBannerAdditionalProviders } from '../../../types/client/adProviders';
import type { ContractCodeIde } from '../../../types/client/contract'; import { SMART_CONTRACT_EXTRA_VERIFICATION_METHODS, type ContractCodeIde, type SmartContractVerificationMethodExtra } from '../../../types/client/contract';
import type { DeFiDropdownItem } from '../../../types/client/deFiDropdown'; import type { DeFiDropdownItem } from '../../../types/client/deFiDropdown';
import type { GasRefuelProviderConfig } from '../../../types/client/gasRefuelProviderConfig'; import type { GasRefuelProviderConfig } from '../../../types/client/gasRefuelProviderConfig';
import { GAS_UNITS } from '../../../types/client/gasTracker'; import { GAS_UNITS } from '../../../types/client/gasTracker';
...@@ -576,6 +577,21 @@ const schema = yup ...@@ -576,6 +577,21 @@ const schema = yup
.json() .json()
.of(yup.string<AddressViewId>().oneOf(ADDRESS_VIEWS_IDS)), .of(yup.string<AddressViewId>().oneOf(ADDRESS_VIEWS_IDS)),
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: yup.boolean(), NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: yup.boolean(),
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS: yup
.mixed()
.test(
'shape',
'Invalid schema were provided for NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS, it should be either array of method ids or "none" string literal',
(data) => {
const isNoneSchema = yup.string().oneOf([ 'none' ]);
const isArrayOfMethodsSchema = yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<SmartContractVerificationMethodExtra>().oneOf(SMART_CONTRACT_EXTRA_VERIFICATION_METHODS));
return isNoneSchema.isValidSync(data) || isArrayOfMethodsSchema.isValidSync(data);
}),
NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS: yup NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS: yup
.array() .array()
.transform(replaceQuotes) .transform(replaceQuotes)
......
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none
NEXT_PUBLIC_API_SPEC_URL=none NEXT_PUBLIC_API_SPEC_URL=none
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none
\ No newline at end of file
...@@ -68,6 +68,7 @@ NEXT_PUBLIC_STATS_API_BASE_PATH=/ ...@@ -68,6 +68,7 @@ NEXT_PUBLIC_STATS_API_BASE_PATH=/
NEXT_PUBLIC_USE_NEXT_JS_PROXY=false NEXT_PUBLIC_USE_NEXT_JS_PROXY=false
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar
NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS=['top_accounts'] NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS=['top_accounts']
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=['solidity-hardhat','solidity-foundry']
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward'] NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward']
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'NFT Marketplace','collection_url':'https://example.com/{hash}','instance_url':'https://example.com/{hash}/{id}','logo_url':'https://example.com/logo.png'}] NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'NFT Marketplace','collection_url':'https://example.com/{hash}','instance_url':'https://example.com/{hash}/{id}','logo_url':'https://example.com/logo.png'}]
NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS=['fee_per_gas'] NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS=['fee_per_gas']
......
...@@ -221,6 +221,7 @@ Settings for meta tags, OG tags and SEO ...@@ -221,6 +221,7 @@ Settings for meta tags, OG tags and SEO
| NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE | `"github" \| "jazzicon" \| "gradient_avatar" \| "blockie"` | Default style of address identicon appearance. Choose between [GitHub](https://github.blog/2013-08-14-identicons/), [Metamask Jazzicon](https://metamask.github.io/jazzicon/), [Gradient Avatar](https://github.com/varld/gradient-avatar) and [Ethereum Blocky](https://mycryptohq.github.io/ethereum-blockies-base64/) | - | `jazzicon` | `gradient_avatar` | v1.12.0+ | | NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE | `"github" \| "jazzicon" \| "gradient_avatar" \| "blockie"` | Default style of address identicon appearance. Choose between [GitHub](https://github.blog/2013-08-14-identicons/), [Metamask Jazzicon](https://metamask.github.io/jazzicon/), [Gradient Avatar](https://github.com/varld/gradient-avatar) and [Ethereum Blocky](https://mycryptohq.github.io/ethereum-blockies-base64/) | - | `jazzicon` | `gradient_avatar` | v1.12.0+ |
| NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS | `Array<AddressViewId>` | Address views that should not be displayed. See below the list of the possible id values. | - | - | `'["top_accounts"]'` | v1.15.0+ | | NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS | `Array<AddressViewId>` | Address views that should not be displayed. See below the list of the possible id values. | - | - | `'["top_accounts"]'` | v1.15.0+ |
| NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED | `boolean` | Set to `true` if SolidityScan reports are supported | - | - | `true` | v1.19.0+ | | NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED | `boolean` | Set to `true` if SolidityScan reports are supported | - | - | `true` | v1.19.0+ |
| NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS | `Array<'solidity-hardhat' \| 'solidity-foundry'>` | Pass an array of additional methods from which users can choose while verifying a smart contract. Both methods are available by default, pass `'none'` string to disable them all. | - | - | `['solidity-hardhat']` | v1.33.0+ |
##### Address views list ##### Address views list
| Id | Description | | Id | Description |
......
...@@ -54,7 +54,7 @@ import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransac ...@@ -54,7 +54,7 @@ import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransac
import type { BackendVersionConfig } from 'types/api/configs'; import type { BackendVersionConfig } from 'types/api/configs';
import type { import type {
SmartContract, SmartContract,
SmartContractVerificationConfig, SmartContractVerificationConfigRaw,
SolidityscanReport, SolidityscanReport,
SmartContractSecurityAudits, SmartContractSecurityAudits,
} from 'types/api/contract'; } from 'types/api/contract';
...@@ -1028,7 +1028,7 @@ Q extends 'contract_solidityscan_report' ? SolidityscanReport : ...@@ -1028,7 +1028,7 @@ Q extends 'contract_solidityscan_report' ? SolidityscanReport :
Q extends 'verified_contracts' ? VerifiedContractsResponse : Q extends 'verified_contracts' ? VerifiedContractsResponse :
Q extends 'verified_contracts_counters' ? VerifiedContractsCounters : Q extends 'verified_contracts_counters' ? VerifiedContractsCounters :
Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse : Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse :
Q extends 'contract_verification_config' ? SmartContractVerificationConfig : Q extends 'contract_verification_config' ? SmartContractVerificationConfigRaw :
Q extends 'withdrawals' ? WithdrawalsResponse : Q extends 'withdrawals' ? WithdrawalsResponse :
Q extends 'withdrawals_counters' ? WithdrawalsCounters : Q extends 'withdrawals_counters' ? WithdrawalsCounters :
Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse : Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
......
...@@ -75,7 +75,7 @@ export interface SmartContractExternalLibrary { ...@@ -75,7 +75,7 @@ export interface SmartContractExternalLibrary {
// VERIFICATION // VERIFICATION
export type SmartContractVerificationMethod = 'flattened-code' | 'standard-input' | 'sourcify' | 'multi-part' export type SmartContractVerificationMethodApi = 'flattened-code' | 'standard-input' | 'sourcify' | 'multi-part'
| 'vyper-code' | 'vyper-multi-part' | 'vyper-standard-input'; | 'vyper-code' | 'vyper-multi-part' | 'vyper-standard-input';
export interface SmartContractVerificationConfigRaw { export interface SmartContractVerificationConfigRaw {
...@@ -88,10 +88,6 @@ export interface SmartContractVerificationConfigRaw { ...@@ -88,10 +88,6 @@ export interface SmartContractVerificationConfigRaw {
license_types: Record<SmartContractLicenseType, number>; license_types: Record<SmartContractLicenseType, number>;
} }
export interface SmartContractVerificationConfig extends SmartContractVerificationConfigRaw {
verification_options: Array<SmartContractVerificationMethod>;
}
export type SmartContractVerificationResponse = { export type SmartContractVerificationResponse = {
status: 'error'; status: 'error';
errors: SmartContractVerificationError; errors: SmartContractVerificationError;
......
import type { SmartContractLicenseType } from 'types/api/contract'; import type { SmartContractLicenseType, SmartContractVerificationConfigRaw, SmartContractVerificationMethodApi } from 'types/api/contract';
export interface ContractCodeIde { export interface ContractCodeIde {
title: string; title: string;
...@@ -12,3 +12,16 @@ export interface ContractLicense { ...@@ -12,3 +12,16 @@ export interface ContractLicense {
label: string; label: string;
title: string; title: string;
} }
export const SMART_CONTRACT_EXTRA_VERIFICATION_METHODS = [
'solidity-hardhat' as const,
'solidity-foundry' as const,
];
export type SmartContractVerificationMethodExtra = (typeof SMART_CONTRACT_EXTRA_VERIFICATION_METHODS)[number];
export type SmartContractVerificationMethod = SmartContractVerificationMethodApi | SmartContractVerificationMethodExtra;
export interface SmartContractVerificationConfig extends SmartContractVerificationConfigRaw {
verification_options: Array<SmartContractVerificationMethod>;
}
import React from 'react'; import React from 'react';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import * as socketServer from 'playwright/fixtures/socketServer'; import * as socketServer from 'playwright/fixtures/socketServer';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
...@@ -37,6 +37,8 @@ const formConfig: SmartContractVerificationConfig = { ...@@ -37,6 +37,8 @@ const formConfig: SmartContractVerificationConfig = {
'vyper-code', 'vyper-code',
'vyper-multi-part', 'vyper-multi-part',
'vyper-standard-input', 'vyper-standard-input',
'solidity-hardhat',
'solidity-foundry',
], ],
vyper_compiler_versions: [ vyper_compiler_versions: [
'v0.3.7+commit.6020b8bb', 'v0.3.7+commit.6020b8bb',
...@@ -82,7 +84,7 @@ test('flatten source code method +@dark-mode +@mobile', async({ render, page }) ...@@ -82,7 +84,7 @@ test('flatten source code method +@dark-mode +@mobile', async({ render, page })
// select method // select method
await component.getByLabel(/verification method/i).focus(); await component.getByLabel(/verification method/i).focus();
await component.getByLabel(/verification method/i).fill('solidity'); await component.getByLabel(/verification method/i).fill('solidity');
await page.getByRole('button', { name: /flattened source code/i }).click(); await page.getByRole('button', { name: /single file/i }).click();
await page.getByText(/add contract libraries/i).click(); await page.getByText(/add contract libraries/i).click();
await page.locator('button[aria-label="add"]').click(); await page.locator('button[aria-label="add"]').click();
...@@ -191,3 +193,25 @@ test('vyper vyper-standard-input method', async({ render, page }) => { ...@@ -191,3 +193,25 @@ test('vyper vyper-standard-input method', async({ render, page }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('solidity-hardhat method', async({ render, page }) => {
const component = await render(<ContractVerificationForm config={ formConfig } hash={ hash }/>, { hooksConfig });
// select method
await component.getByLabel(/verification method/i).focus();
await component.getByLabel(/verification method/i).fill('hardhat');
await page.getByRole('button', { name: /hardhat/i }).click();
await expect(component).toHaveScreenshot();
});
test('solidity-foundry method', async({ render, page }) => {
const component = await render(<ContractVerificationForm config={ formConfig } hash={ hash }/>, { hooksConfig });
// select method
await component.getByLabel(/verification method/i).focus();
await component.getByLabel(/verification method/i).fill('foundry');
await page.getByRole('button', { name: /foundry/i }).click();
await expect(component).toHaveScreenshot();
});
...@@ -5,7 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form'; ...@@ -5,7 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form';
import type { FormFields } from './types'; import type { FormFields } from './types';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { SmartContractVerificationMethod, SmartContractVerificationConfig, SmartContract } from 'types/api/contract'; import type { SmartContract, SmartContractVerificationMethodApi } from 'types/api/contract';
import type { SmartContractVerificationConfig } from 'types/client/contract';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -22,6 +23,8 @@ import ContractVerificationFieldLicenseType from './fields/ContractVerificationF ...@@ -22,6 +23,8 @@ import ContractVerificationFieldLicenseType from './fields/ContractVerificationF
import ContractVerificationFieldMethod from './fields/ContractVerificationFieldMethod'; import ContractVerificationFieldMethod from './fields/ContractVerificationFieldMethod';
import ContractVerificationFlattenSourceCode from './methods/ContractVerificationFlattenSourceCode'; import ContractVerificationFlattenSourceCode from './methods/ContractVerificationFlattenSourceCode';
import ContractVerificationMultiPartFile from './methods/ContractVerificationMultiPartFile'; import ContractVerificationMultiPartFile from './methods/ContractVerificationMultiPartFile';
import ContractVerificationSolidityFoundry from './methods/ContractVerificationSolidityFoundry';
import ContractVerificationSolidityHardhat from './methods/ContractVerificationSolidityHardhat';
import ContractVerificationSourcify from './methods/ContractVerificationSourcify'; import ContractVerificationSourcify from './methods/ContractVerificationSourcify';
import ContractVerificationStandardInput from './methods/ContractVerificationStandardInput'; import ContractVerificationStandardInput from './methods/ContractVerificationStandardInput';
import ContractVerificationVyperContract from './methods/ContractVerificationVyperContract'; import ContractVerificationVyperContract from './methods/ContractVerificationVyperContract';
...@@ -30,7 +33,7 @@ import ContractVerificationVyperStandardInput from './methods/ContractVerificati ...@@ -30,7 +33,7 @@ import ContractVerificationVyperStandardInput from './methods/ContractVerificati
import { prepareRequestBody, formatSocketErrors, getDefaultValues, METHOD_LABELS } from './utils'; import { prepareRequestBody, formatSocketErrors, getDefaultValues, METHOD_LABELS } from './utils';
interface Props { interface Props {
method?: SmartContractVerificationMethod; method?: SmartContractVerificationMethodApi;
config: SmartContractVerificationConfig; config: SmartContractVerificationConfig;
hash?: string; hash?: string;
} }
...@@ -159,6 +162,8 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro ...@@ -159,6 +162,8 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
'vyper-code': <ContractVerificationVyperContract config={ config }/>, 'vyper-code': <ContractVerificationVyperContract config={ config }/>,
'vyper-multi-part': <ContractVerificationVyperMultiPartFile/>, 'vyper-multi-part': <ContractVerificationVyperMultiPartFile/>,
'vyper-standard-input': <ContractVerificationVyperStandardInput/>, 'vyper-standard-input': <ContractVerificationVyperStandardInput/>,
'solidity-hardhat': <ContractVerificationSolidityHardhat config={ config }/>,
'solidity-foundry': <ContractVerificationSolidityFoundry/>,
}; };
}, [ config ]); }, [ config ]);
const method = watch('method'); const method = watch('method');
...@@ -193,7 +198,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro ...@@ -193,7 +198,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
/> />
</Grid> </Grid>
{ content } { content }
{ Boolean(method) && ( { Boolean(method) && method.value !== 'solidity-hardhat' && method.value !== 'solidity-foundry' && (
<Button <Button
variant="solid" variant="solid"
size="lg" size="lg"
......
import { Code } from '@chakra-ui/react';
import React from 'react';
interface Props {
code: string;
}
const ContractVerificationFormCodeSnippet = ({ code }: Props) => {
return (
<Code whiteSpace="pre-wrap" wordBreak="break-all" p={ 2 } borderRadius="base">
{ code }
</Code>
);
};
export default React.memo(ContractVerificationFormCodeSnippet);
import { FormControl, Link, Textarea } from '@chakra-ui/react'; import { FormControl, Textarea } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form'; import type { ControllerRenderProps } from 'react-hook-form';
import { useFormContext, Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
...@@ -43,12 +43,7 @@ const ContractVerificationFieldCode = ({ isVyper }: Props) => { ...@@ -43,12 +43,7 @@ const ContractVerificationFieldCode = ({ isVyper }: Props) => {
rules={{ required: true }} rules={{ required: true }}
/> />
{ isVyper ? null : ( { isVyper ? null : (
<> <span>If your code utilizes a library or inherits dependencies, we recommend using other verification methods instead.</span>
<span>We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the </span>
<Link href="https://hardhat.org/hardhat-runner/docs/advanced/flattening" target="_blank">Hardhat flattener</Link>
<span> or the </span>
<Link href="https://www.npmjs.com/package/truffle-flattener" target="_blank">Truffle flattener</Link>
</>
) } ) }
</ContractVerificationFormRow> </ContractVerificationFormRow>
); );
......
...@@ -5,7 +5,7 @@ import type { ControllerRenderProps } from 'react-hook-form'; ...@@ -5,7 +5,7 @@ import type { ControllerRenderProps } from 'react-hook-form';
import { useFormContext, Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
......
...@@ -5,7 +5,7 @@ import type { ControllerRenderProps } from 'react-hook-form'; ...@@ -5,7 +5,7 @@ import type { ControllerRenderProps } from 'react-hook-form';
import { useFormContext, Controller } from 'react-hook-form'; import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
......
...@@ -17,7 +17,7 @@ import type { ControllerRenderProps, Control } from 'react-hook-form'; ...@@ -17,7 +17,7 @@ import type { ControllerRenderProps, Control } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
import type { SmartContractVerificationConfig, SmartContractVerificationMethod } from 'types/api/contract'; import type { SmartContractVerificationMethod, SmartContractVerificationConfig } from 'types/client/contract';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import Popover from 'ui/shared/chakra/Popover'; import Popover from 'ui/shared/chakra/Popover';
...@@ -58,7 +58,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props ...@@ -58,7 +58,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props
const renderPopoverListItem = React.useCallback((method: SmartContractVerificationMethod) => { const renderPopoverListItem = React.useCallback((method: SmartContractVerificationMethod) => {
switch (method) { switch (method) {
case 'flattened-code': case 'flattened-code':
return <ListItem key={ method }>Verification through flattened source code.</ListItem>; return <ListItem key={ method }>Verification through a single file.</ListItem>;
case 'multi-part': case 'multi-part':
return <ListItem key={ method }>Verification of multi-part Solidity files.</ListItem>; return <ListItem key={ method }>Verification of multi-part Solidity files.</ListItem>;
case 'sourcify': case 'sourcify':
...@@ -93,6 +93,10 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props ...@@ -93,6 +93,10 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props
<span> file.</span> <span> file.</span>
</ListItem> </ListItem>
); );
case 'solidity-hardhat':
return <ListItem key={ method }>Verification through Hardhat plugin.</ListItem>;
case 'solidity-foundry':
return <ListItem key={ method }>Verification through Foundry.</ListItem>;
} }
}, []); }, []);
......
import React from 'react'; import React from 'react';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs'; import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs';
......
import { Box, Flex, Link } from '@chakra-ui/react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import type { FormFields } from '../types';
import config from 'configs/app';
import ContractVerificationFormCodeSnippet from '../ContractVerificationFormCodeSnippet';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
import ContractVerificationMethod from '../ContractVerificationMethod';
const ContractVerificationSolidityFoundry = () => {
const { watch } = useFormContext<FormFields>();
const address = watch('address');
const codeSnippet = `forge verify-contract \\
--rpc-url ${ config.chain.rpcUrl || `${ config.api.endpoint }/api/eth-rpc` } \\
${ address || '<address>' } \\
[contractFile]:[contractName] \\
--verifier blockscout \\
--verifier-url '${ config.api.endpoint }/api/'`;
return (
<ContractVerificationMethod title="Contract verification via Foundry">
<ContractVerificationFormRow>
<Flex flexDir="column">
<ContractVerificationFormCodeSnippet code={ codeSnippet }/>
</Flex>
<Box whiteSpace="pre-wrap">
<span>Full tutorial about contract verification via Foundry on Blockscout is available </span>
<Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract/foundry-verification" target="_blank">
here
</Link>
</Box>
</ContractVerificationFormRow>
</ContractVerificationMethod>
);
};
export default React.memo(ContractVerificationSolidityFoundry);
import { Box, Flex, Link } from '@chakra-ui/react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/client/contract';
import config from 'configs/app';
import ContractVerificationFormCodeSnippet from '../ContractVerificationFormCodeSnippet';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
import ContractVerificationMethod from '../ContractVerificationMethod';
const ContractVerificationSolidityHardhat = ({ config: formConfig }: { config: SmartContractVerificationConfig }) => {
const chainNameSlug = config.chain.name?.toLowerCase().split(' ').join('-');
const { watch } = useFormContext<FormFields>();
const address = watch('address');
const latestSolidityVersion = formConfig.solidity_compiler_versions.find((version) => !version.includes('nightly'))?.split('+')[0];
const firstCodeSnippet = `const config: HardhatUserConfig = {
solidity: "${ latestSolidityVersion || '0.8.24' }", // replace if necessary
networks: {
'${ chainNameSlug }': {
url: '${ config.chain.rpcUrl || `${ config.api.endpoint }/api/eth-rpc` }'
},
},
etherscan: {
apiKey: {
'${ chainNameSlug }': 'empty'
},
customChains: [
{
network: "${ chainNameSlug }",
chainId: ${ config.chain.id },
urls: {
apiURL: "${ config.api.endpoint }/api",
browserURL: "${ config.app.baseUrl }"
}
}
]
}
};`;
const secondCodeSnippet = `npx hardhat verify \\
--network ${ chainNameSlug } \\
${ address || '<address>' } \\
[...constructorArgs]`;
return (
<ContractVerificationMethod title="Contract verification via Solidity Hardhat plugin">
<ContractVerificationFormRow>
<Flex flexDir="column" rowGap={ 3 }>
<ContractVerificationFormCodeSnippet code={ firstCodeSnippet }/>
<ContractVerificationFormCodeSnippet code={ secondCodeSnippet }/>
</Flex>
<Box whiteSpace="pre-wrap">
<span>Full tutorial about contract verification via Hardhat on Blockscout is available </span>
<Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract/hardhat-verification-plugin" target="_blank">
here
</Link>
</Box>
</ContractVerificationFormRow>
</ContractVerificationMethod>
);
};
export default React.memo(ContractVerificationSolidityHardhat);
import React from 'react'; import React from 'react';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs'; import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs';
......
import React from 'react'; import React from 'react';
import type { SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCode'; import ContractVerificationFieldCode from '../fields/ContractVerificationFieldCode';
......
import type { SmartContractLicenseType, SmartContractVerificationMethod } from 'types/api/contract'; import type { SmartContractLicenseType } from 'types/api/contract';
import type { SmartContractVerificationMethod } from 'types/client/contract';
import type { Option } from 'ui/shared/FancySelect/types'; import type { Option } from 'ui/shared/FancySelect/types';
export interface ContractLibrary { export interface ContractLibrary {
......
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { isValidVerificationMethod, sortVerificationMethods } from './utils'; import { isValidVerificationMethod, sortVerificationMethods } from './utils';
...@@ -8,7 +9,10 @@ export default function useFormConfigQuery(enabled: boolean) { ...@@ -8,7 +9,10 @@ export default function useFormConfigQuery(enabled: boolean) {
select: (data) => { select: (data) => {
return { return {
...data, ...data,
verification_options: data.verification_options.filter(isValidVerificationMethod).sort(sortVerificationMethods), verification_options: [
...data.verification_options,
...config.UI.views.address.extraVerificationMethods,
].filter(isValidVerificationMethod).sort(sortVerificationMethods),
}; };
}, },
enabled, enabled,
......
...@@ -12,11 +12,10 @@ import type { ...@@ -12,11 +12,10 @@ import type {
FormFieldsVyperStandardInput, FormFieldsVyperStandardInput,
} from './types'; } from './types';
import type { import type {
SmartContractVerificationMethod,
SmartContractVerificationError, SmartContractVerificationError,
SmartContractVerificationConfig,
SmartContractLicenseType, SmartContractLicenseType,
} from 'types/api/contract'; } from 'types/api/contract';
import type { SmartContractVerificationConfig, SmartContractVerificationMethod } from 'types/client/contract';
import type { Params as FetchParams } from 'lib/hooks/useFetch'; import type { Params as FetchParams } from 'lib/hooks/useFetch';
...@@ -25,19 +24,23 @@ export const SUPPORTED_VERIFICATION_METHODS: Array<SmartContractVerificationMeth ...@@ -25,19 +24,23 @@ export const SUPPORTED_VERIFICATION_METHODS: Array<SmartContractVerificationMeth
'standard-input', 'standard-input',
'sourcify', 'sourcify',
'multi-part', 'multi-part',
'solidity-hardhat',
'solidity-foundry',
'vyper-code', 'vyper-code',
'vyper-multi-part', 'vyper-multi-part',
'vyper-standard-input', 'vyper-standard-input',
]; ];
export const METHOD_LABELS: Record<SmartContractVerificationMethod, string> = { export const METHOD_LABELS: Record<SmartContractVerificationMethod, string> = {
'flattened-code': 'Solidity (Flattened source code)', 'flattened-code': 'Solidity (Single file)',
'standard-input': 'Solidity (Standard JSON input)', 'standard-input': 'Solidity (Standard JSON input)',
sourcify: 'Solidity (Sourcify)', sourcify: 'Solidity (Sourcify)',
'multi-part': 'Solidity (Multi-part files)', 'multi-part': 'Solidity (Multi-part files)',
'vyper-code': 'Vyper (Contract)', 'vyper-code': 'Vyper (Contract)',
'vyper-multi-part': 'Vyper (Multi-part files)', 'vyper-multi-part': 'Vyper (Multi-part files)',
'vyper-standard-input': 'Vyper (Standard JSON input)', 'vyper-standard-input': 'Vyper (Standard JSON input)',
'solidity-hardhat': 'Solidity (Hardhat)',
'solidity-foundry': 'Solidity (Foundry)',
}; };
export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields> = { export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields> = {
...@@ -129,6 +132,26 @@ export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields> ...@@ -129,6 +132,26 @@ export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields>
sources: [], sources: [],
license_type: null, license_type: null,
}, },
'solidity-hardhat': {
address: '',
method: {
value: 'solidity-hardhat' as const,
label: METHOD_LABELS['solidity-hardhat'],
},
compiler: null,
sources: [],
license_type: null,
},
'solidity-foundry': {
address: '',
method: {
value: 'solidity-foundry' as const,
label: METHOD_LABELS['solidity-foundry'],
},
compiler: null,
sources: [],
license_type: null,
},
}; };
export function getDefaultValues( export function getDefaultValues(
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { SmartContractVerificationMethod } from 'types/api/contract'; import type { SmartContractVerificationMethodApi } from 'types/api/contract';
import type { SmartContractVerificationMethod } from 'types/client/contract';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
...@@ -59,7 +60,7 @@ const ContractVerificationForAddress = () => { ...@@ -59,7 +60,7 @@ const ContractVerificationForAddress = () => {
return ( return (
<ContractVerificationForm <ContractVerificationForm
method={ method && configQuery.data.verification_options.includes(method) ? method : undefined } method={ method && configQuery.data.verification_options.includes(method) ? method as SmartContractVerificationMethodApi : undefined }
config={ configQuery.data } config={ configQuery.data }
hash={ hash } hash={ hash }
/> />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment