Commit f58a3e9c authored by tom goriunov's avatar tom goriunov Committed by GitHub

Add support for zkSync smart-contracts (#2173)

* additional form fields

* pass optimization modes from API and handle single verification method case

* display zk contract info
parent 5df91795
...@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development ...@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs # Instance ENVs
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=zksync.blockscout.com NEXT_PUBLIC_API_HOST=zksync.blockscout.com
......
...@@ -99,6 +99,13 @@ export const withChangedByteCode: SmartContract = { ...@@ -99,6 +99,13 @@ export const withChangedByteCode: SmartContract = {
is_blueprint: true, is_blueprint: true,
}; };
export const zkSync: SmartContract = {
...verified,
zk_compiler_version: 'v1.2.5',
optimization_enabled: true,
optimization_runs: 's',
};
export const nonVerified: SmartContract = { export const nonVerified: SmartContract = {
is_verified: false, is_verified: false,
is_blueprint: false, is_blueprint: false,
......
...@@ -37,6 +37,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = { ...@@ -37,6 +37,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
zkSyncRollup: [ zkSyncRollup: [
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkSync' ], [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkSync' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ], [ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
[ 'NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS', 'none' ],
], ],
bridgedTokens: [ bridgedTokens: [
[ 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS', '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]' ], [ 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS', '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]' ],
......
...@@ -27,7 +27,7 @@ export interface SmartContract { ...@@ -27,7 +27,7 @@ export interface SmartContract {
compiler_version: string | null; compiler_version: string | null;
evm_version: string | null; evm_version: string | null;
optimization_enabled: boolean | null; optimization_enabled: boolean | null;
optimization_runs: number | null; optimization_runs: number | string | null;
name: string | null; name: string | null;
verified_at: string | null; verified_at: string | null;
is_blueprint: boolean | null; is_blueprint: boolean | null;
...@@ -57,6 +57,7 @@ export interface SmartContract { ...@@ -57,6 +57,7 @@ export interface SmartContract {
language: string | null; language: string | null;
license_type: SmartContractLicenseType | null; license_type: SmartContractLicenseType | null;
certified?: boolean; certified?: boolean;
zk_compiler_version?: string;
} }
export type SmartContractDecodedConstructorArg = [ export type SmartContractDecodedConstructorArg = [
...@@ -86,6 +87,8 @@ export interface SmartContractVerificationConfigRaw { ...@@ -86,6 +87,8 @@ export interface SmartContractVerificationConfigRaw {
vyper_evm_versions: Array<string>; vyper_evm_versions: Array<string>;
is_rust_verifier_microservice_enabled: boolean; is_rust_verifier_microservice_enabled: boolean;
license_types: Record<SmartContractLicenseType, number>; license_types: Record<SmartContractLicenseType, number>;
zk_compiler_versions?: Array<string>;
zk_optimization_modes?: Array<string>;
} }
export type SmartContractVerificationResponse = { export type SmartContractVerificationResponse = {
......
...@@ -13,6 +13,7 @@ export interface VerifiedContract { ...@@ -13,6 +13,7 @@ export interface VerifiedContract {
verified_at: string; verified_at: string;
market_cap: string | null; market_cap: string | null;
license_type: SmartContractLicenseType | null; license_type: SmartContractLicenseType | null;
zk_compiler_version?: string;
} }
export interface VerifiedContractsResponse { export interface VerifiedContractsResponse {
......
...@@ -126,6 +126,14 @@ test('non verified', async({ render, mockApiResponse }) => { ...@@ -126,6 +126,14 @@ test('non verified', async({ render, mockApiResponse }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('zkSync contract', async({ render, mockApiResponse, page, mockEnvs }) => {
await mockEnvs(ENVS_MAP.zkSyncRollup);
await mockApiResponse('contract', contractMock.zkSync, { pathParams: { hash: addressMock.contract.hash } });
await render(<ContractCode/>, { hooksConfig }, { withSocket: true });
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 300 } });
});
test.describe('with audits feature', () => { test.describe('with audits feature', () => {
test.beforeEach(async({ mockEnvs }) => { test.beforeEach(async({ mockEnvs }) => {
......
...@@ -60,6 +60,8 @@ const InfoItem = chakra(({ label, content, hint, className, isLoading }: InfoIte ...@@ -60,6 +60,8 @@ const InfoItem = chakra(({ label, content, hint, className, isLoading }: InfoIte
</GridItem> </GridItem>
)); ));
const rollupFeature = config.features.rollup;
const ContractCode = ({ addressHash, contractQuery, channel }: Props) => { const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>(); const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>();
...@@ -266,6 +268,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => { ...@@ -266,6 +268,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
<Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }> <Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }>
{ data.name && <InfoItem label="Contract name" content={ contractNameWithCertifiedIcon } isLoading={ isPlaceholderData }/> } { data.name && <InfoItem label="Contract name" content={ contractNameWithCertifiedIcon } isLoading={ isPlaceholderData }/> }
{ data.compiler_version && <InfoItem label="Compiler version" content={ data.compiler_version } isLoading={ isPlaceholderData }/> } { data.compiler_version && <InfoItem label="Compiler version" content={ data.compiler_version } isLoading={ isPlaceholderData }/> }
{ data.zk_compiler_version && <InfoItem label="ZK compiler version" content={ data.zk_compiler_version } isLoading={ isPlaceholderData }/> }
{ data.evm_version && <InfoItem label="EVM version" content={ data.evm_version } textTransform="capitalize" isLoading={ isPlaceholderData }/> } { data.evm_version && <InfoItem label="EVM version" content={ data.evm_version } textTransform="capitalize" isLoading={ isPlaceholderData }/> }
{ licenseLink && ( { licenseLink && (
<InfoItem <InfoItem
...@@ -277,8 +280,13 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => { ...@@ -277,8 +280,13 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
) } ) }
{ typeof data.optimization_enabled === 'boolean' && { typeof data.optimization_enabled === 'boolean' &&
<InfoItem label="Optimization enabled" content={ data.optimization_enabled ? 'true' : 'false' } isLoading={ isPlaceholderData }/> } <InfoItem label="Optimization enabled" content={ data.optimization_enabled ? 'true' : 'false' } isLoading={ isPlaceholderData }/> }
{ data.optimization_runs !== null && { data.optimization_runs !== null && (
<InfoItem label="Optimization runs" content={ String(data.optimization_runs) } isLoading={ isPlaceholderData }/> } <InfoItem
label={ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' ? 'Optimization mode' : 'Optimization runs' }
content={ String(data.optimization_runs) }
isLoading={ isPlaceholderData }
/>
) }
{ data.verified_at && { data.verified_at &&
<InfoItem label="Verified at" content={ dayjs(data.verified_at).format('llll') } wordBreak="break-word" isLoading={ isPlaceholderData }/> } <InfoItem label="Verified at" content={ dayjs(data.verified_at).format('llll') } wordBreak="break-word" isLoading={ isPlaceholderData }/> }
{ data.file_path && <InfoItem label="Contract file path" content={ data.file_path } wordBreak="break-word" isLoading={ isPlaceholderData }/> } { data.file_path && <InfoItem label="Contract file path" content={ data.file_path } wordBreak="break-word" isLoading={ isPlaceholderData }/> }
......
...@@ -2,6 +2,7 @@ import React from 'react'; ...@@ -2,6 +2,7 @@ import React from 'react';
import type { SmartContractVerificationConfig } from 'types/client/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
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';
...@@ -215,3 +216,17 @@ test('solidity-foundry method', async({ render, page }) => { ...@@ -215,3 +216,17 @@ test('solidity-foundry method', async({ render, page }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('verification of zkSync contract', async({ render, mockEnvs }) => {
const zkSyncFormConfig: SmartContractVerificationConfig = {
...formConfig,
verification_options: [ 'standard-input' ],
zk_compiler_versions: [ 'v1.4.1', 'v1.4.0', 'v1.3.23', 'v1.3.22' ],
zk_optimization_modes: [ '0', '1', '2', '3', 's', 'z' ],
};
await mockEnvs(ENVS_MAP.zkSyncRollup);
const component = await render(<ContractVerificationForm config={ zkSyncFormConfig } hash={ hash }/>, { hooksConfig });
await expect(component).toHaveScreenshot();
});
...@@ -41,7 +41,7 @@ interface Props { ...@@ -41,7 +41,7 @@ interface Props {
const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Props) => { const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Props) => {
const formApi = useForm<FormFields>({ const formApi = useForm<FormFields>({
mode: 'onBlur', mode: 'onBlur',
defaultValues: methodFromQuery ? getDefaultValues(methodFromQuery, config, hash, null) : undefined, defaultValues: getDefaultValues(methodFromQuery, config, hash, null),
}); });
const { control, handleSubmit, watch, formState, setError, reset, getFieldState } = formApi; const { control, handleSubmit, watch, formState, setError, reset, getFieldState } = formApi;
const submitPromiseResolver = React.useRef<(value: unknown) => void>(); const submitPromiseResolver = React.useRef<(value: unknown) => void>();
......
...@@ -4,14 +4,15 @@ import React from 'react'; ...@@ -4,14 +4,15 @@ import React from 'react';
interface Props { interface Props {
title: string; title: string;
children: React.ReactNode; children: React.ReactNode;
disableScroll?: boolean;
} }
const ContractVerificationMethod = ({ title, children }: Props) => { const ContractVerificationMethod = ({ title, children, disableScroll }: Props) => {
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => { React.useEffect(() => {
ref.current?.scrollIntoView({ behavior: 'smooth' }); !disableScroll && ref.current?.scrollIntoView({ behavior: 'smooth' });
}, []); }, [ disableScroll ]);
return ( return (
<section ref={ ref }> <section ref={ ref }>
......
...@@ -51,6 +51,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props ...@@ -51,6 +51,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props
isDisabled={ isDisabled } isDisabled={ isDisabled }
isRequired isRequired
isAsync={ false } isAsync={ false }
isReadOnly={ options.length === 1 }
/> />
); );
}, [ isDisabled, isMobile, options ]); }, [ isDisabled, isMobile, options ]);
......
import { Box, Link } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { useFormContext, Controller } from 'react-hook-form';
import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/client/contract';
import { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import FancySelect from 'ui/shared/FancySelect/FancySelect';
import IconSvg from 'ui/shared/IconSvg';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
const OPTIONS_LIMIT = 50;
const ContractVerificationFieldZkCompiler = () => {
const { formState, control } = useFormContext<FormFields>();
const isMobile = useIsMobile();
const queryClient = useQueryClient();
const config = queryClient.getQueryData<SmartContractVerificationConfig>(getResourceKey('contract_verification_config'));
const options = React.useMemo(() => (
config?.zk_compiler_versions?.map((option) => ({ label: option, value: option })) || []
), [ config?.zk_compiler_versions ]);
const loadOptions = React.useCallback(async(inputValue: string) => {
return options
.filter(({ label }) => !inputValue || label.toLowerCase().includes(inputValue.toLowerCase()))
.slice(0, OPTIONS_LIMIT);
}, [ options ]);
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'zk_compiler'>}) => {
const error = 'zk_compiler' in formState.errors ? formState.errors.zk_compiler : undefined;
return (
<FancySelect
{ ...field }
loadOptions={ loadOptions }
defaultOptions
size={ isMobile ? 'md' : 'lg' }
placeholder="ZK compiler (enter version or use the dropdown)"
placeholderIcon={ <IconSvg name="search"/> }
isDisabled={ formState.isSubmitting }
error={ error }
isRequired
isAsync
/>
);
}, [ formState.errors, formState.isSubmitting, isMobile, loadOptions ]);
return (
<ContractVerificationFormRow>
<Controller
name="zk_compiler"
control={ control }
render={ renderControl }
rules={{ required: true }}
/>
<Box>
<Link isExternal href="https://docs.zksync.io/zk-stack/components/compiler/specification#glossary">zksolc</Link>
<span> compiler version.</span>
</Box>
</ContractVerificationFormRow>
);
};
export default React.memo(ContractVerificationFieldZkCompiler);
import { Flex, Select } from '@chakra-ui/react';
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types';
import type { SmartContractVerificationConfig } from 'types/client/contract';
import CheckboxInput from 'ui/shared/CheckboxInput';
import ContractVerificationFormRow from '../ContractVerificationFormRow';
interface Props {
config: SmartContractVerificationConfig;
}
const ContractVerificationFieldZkOptimization = ({ config }: Props) => {
const [ isEnabled, setIsEnabled ] = React.useState(false);
const { formState, control } = useFormContext<FormFields>();
const error = 'optimization_mode' in formState.errors ? formState.errors.optimization_mode : undefined;
const handleCheckboxChange = React.useCallback(() => {
setIsEnabled(prev => !prev);
}, []);
const renderCheckboxControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'is_optimization_enabled'>}) => (
<Flex flexShrink={ 0 }>
<CheckboxInput<FormFields, 'is_optimization_enabled'>
text="Optimization enabled"
field={ field }
onChange={ handleCheckboxChange }
isDisabled={ formState.isSubmitting }
/>
</Flex>
), [ formState.isSubmitting, handleCheckboxChange ]);
const renderInputControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'optimization_mode'>}) => {
return (
<Select
size="xs"
{ ...field }
w="auto"
borderRadius="base"
isDisabled={ formState.isSubmitting }
placeholder="Optimization mode"
isInvalid={ Boolean(error) }
>
{ config.zk_optimization_modes?.map((value) => (
<option key={ value } value={ value }>
{ value }
</option>
)) }
</Select>
);
}, [ config.zk_optimization_modes, error, formState.isSubmitting ]);
return (
<ContractVerificationFormRow>
<Flex columnGap={ 5 } rowGap={ 2 } h={{ base: 'auto', lg: '32px' }} flexDir={{ base: 'column', lg: 'row' }}>
<Controller
name="is_optimization_enabled"
control={ control }
render={ renderCheckboxControl }
/>
{ isEnabled && (
<Controller
name="optimization_mode"
control={ control }
render={ renderInputControl }
/>
) }
</Flex>
</ContractVerificationFormRow>
);
};
export default React.memo(ContractVerificationFieldZkOptimization);
...@@ -2,25 +2,32 @@ import React from 'react'; ...@@ -2,25 +2,32 @@ import React from 'react';
import type { SmartContractVerificationConfig } from 'types/client/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract';
import config from 'configs/app';
import ContractVerificationMethod from '../ContractVerificationMethod'; import ContractVerificationMethod from '../ContractVerificationMethod';
import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs'; import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs';
import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler'; import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler';
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName'; import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources'; import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
import ContractVerificationFieldZkCompiler from '../fields/ContractVerificationFieldZkCompiler';
import ContractVerificationFieldZkOptimization from '../fields/ContractVerificationFieldZkOptimization';
const FILE_TYPES = [ '.json' as const ]; const FILE_TYPES = [ '.json' as const ];
const rollupFeature = config.features.rollup;
const ContractVerificationStandardInput = ({ config }: { config: SmartContractVerificationConfig }) => { const ContractVerificationStandardInput = ({ config }: { config: SmartContractVerificationConfig }) => {
return ( return (
<ContractVerificationMethod title="Contract verification via Solidity (standard JSON input) "> <ContractVerificationMethod title="Contract verification via Solidity (standard JSON input) " disableScroll={ config.verification_options.length === 1 }>
{ !config?.is_rust_verifier_microservice_enabled && <ContractVerificationFieldName/> } { !config?.is_rust_verifier_microservice_enabled && <ContractVerificationFieldName/> }
<ContractVerificationFieldCompiler/> <ContractVerificationFieldCompiler/>
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && <ContractVerificationFieldZkCompiler/> }
<ContractVerificationFieldSources <ContractVerificationFieldSources
fileTypes={ FILE_TYPES } fileTypes={ FILE_TYPES }
title="Standard Input JSON" title="Standard Input JSON"
hint="Upload the standard input JSON file created during contract compilation." hint="Upload the standard input JSON file created during contract compilation."
required required
/> />
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && <ContractVerificationFieldZkOptimization config={ config }/> }
{ !config?.is_rust_verifier_microservice_enabled && <ContractVerificationFieldAutodetectArgs/> } { !config?.is_rust_verifier_microservice_enabled && <ContractVerificationFieldAutodetectArgs/> }
</ContractVerificationMethod> </ContractVerificationMethod>
); );
......
...@@ -44,6 +44,20 @@ export interface FormFieldsStandardInput { ...@@ -44,6 +44,20 @@ export interface FormFieldsStandardInput {
license_type: LicenseOption | null; license_type: LicenseOption | null;
} }
export interface FormFieldsStandardInputZk {
address: string;
method: MethodOption;
name: string;
compiler: Option | null;
zk_compiler: Option | null;
sources: Array<File>;
autodetect_constructor_args: boolean;
constructor_args: string;
license_type: LicenseOption | null;
is_optimization_enabled: boolean;
optimization_mode: string | undefined;
}
export interface FormFieldsSourcify { export interface FormFieldsSourcify {
address: string; address: string;
method: MethodOption; method: MethodOption;
...@@ -93,5 +107,5 @@ export interface FormFieldsVyperStandardInput { ...@@ -93,5 +107,5 @@ export interface FormFieldsVyperStandardInput {
license_type: LicenseOption | null; license_type: LicenseOption | null;
} }
export type FormFields = FormFieldsFlattenSourceCode | FormFieldsStandardInput | FormFieldsSourcify | export type FormFields = FormFieldsFlattenSourceCode | FormFieldsStandardInput | FormFieldsStandardInputZk | FormFieldsSourcify |
FormFieldsMultiPartFile | FormFieldsVyperContract | FormFieldsVyperMultiPartFile | FormFieldsVyperStandardInput; FormFieldsMultiPartFile | FormFieldsVyperContract | FormFieldsVyperMultiPartFile | FormFieldsVyperStandardInput;
...@@ -7,6 +7,7 @@ import type { ...@@ -7,6 +7,7 @@ import type {
FormFieldsMultiPartFile, FormFieldsMultiPartFile,
FormFieldsSourcify, FormFieldsSourcify,
FormFieldsStandardInput, FormFieldsStandardInput,
FormFieldsStandardInputZk,
FormFieldsVyperContract, FormFieldsVyperContract,
FormFieldsVyperMultiPartFile, FormFieldsVyperMultiPartFile,
FormFieldsVyperStandardInput, FormFieldsVyperStandardInput,
...@@ -155,11 +156,18 @@ export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields> ...@@ -155,11 +156,18 @@ export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields>
}; };
export function getDefaultValues( export function getDefaultValues(
method: SmartContractVerificationMethod, methodParam: SmartContractVerificationMethod | undefined,
config: SmartContractVerificationConfig, config: SmartContractVerificationConfig,
hash: string | undefined, hash: string | undefined,
licenseType: FormFields['license_type'], licenseType: FormFields['license_type'],
) { ) {
const singleMethod = config.verification_options.length === 1 ? config.verification_options[0] : undefined;
const method = singleMethod || methodParam;
if (!method) {
return;
}
const defaultValues: FormFields = { ...DEFAULT_VALUES[method], address: hash || '', license_type: licenseType }; const defaultValues: FormFields = { ...DEFAULT_VALUES[method], address: hash || '', license_type: licenseType };
if ('evm_version' in defaultValues) { if ('evm_version' in defaultValues) {
...@@ -179,6 +187,13 @@ export function getDefaultValues( ...@@ -179,6 +187,13 @@ export function getDefaultValues(
} }
} }
if (singleMethod) {
defaultValues.method = {
label: METHOD_LABELS[config.verification_options[0]],
value: config.verification_options[0],
};
}
return defaultValues; return defaultValues;
} }
...@@ -223,7 +238,7 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] { ...@@ -223,7 +238,7 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] {
} }
case 'standard-input': { case 'standard-input': {
const _data = data as FormFieldsStandardInput; const _data = data as (FormFieldsStandardInput | FormFieldsStandardInputZk);
const body = new FormData(); const body = new FormData();
_data.compiler && body.set('compiler_version', _data.compiler.value); _data.compiler && body.set('compiler_version', _data.compiler.value);
...@@ -233,6 +248,15 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] { ...@@ -233,6 +248,15 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] {
body.set('constructor_args', _data.constructor_args); body.set('constructor_args', _data.constructor_args);
addFilesToFormData(body, _data.sources, 'files'); addFilesToFormData(body, _data.sources, 'files');
// zkSync fields
'zk_compiler' in _data && _data.zk_compiler && body.set('zk_compiler_version', _data.zk_compiler.value);
if ('is_optimization_enabled' in _data) {
body.set('is_optimization_enabled', String(Boolean(_data.is_optimization_enabled)));
if (_data.is_optimization_enabled && 'optimization_mode' in _data && _data.optimization_mode) {
body.set('optimization_runs', _data.optimization_mode);
}
}
return body; return body;
} }
......
...@@ -70,6 +70,14 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => { ...@@ -70,6 +70,14 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
<Box color="text_secondary" wordBreak="break-all" whiteSpace="pre-wrap"> ({ data.compiler_version })</Box> <Box color="text_secondary" wordBreak="break-all" whiteSpace="pre-wrap"> ({ data.compiler_version })</Box>
</Skeleton> </Skeleton>
</Flex> </Flex>
{ data.zk_compiler_version && (
<Flex columnGap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 } flexShrink="0">ZK compiler</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary" wordBreak="break-all" whiteSpace="pre-wrap">
{ data.zk_compiler_version }
</Skeleton>
</Flex>
) }
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Optimization</Skeleton> <Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Optimization</Skeleton>
{ data.optimization_enabled ? { data.optimization_enabled ?
......
...@@ -70,6 +70,14 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => { ...@@ -70,6 +70,14 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
</Tooltip> </Tooltip>
</Skeleton> </Skeleton>
</Flex> </Flex>
{ data.zk_compiler_version && (
<Flex flexWrap="wrap" columnGap={ 2 } my={ 1 }>
<Skeleton isLoaded={ !isLoading } >ZK compiler</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary" wordBreak="break-all">
<span>{ data.zk_compiler_version }</span>
</Skeleton>
</Flex>
) }
</Td> </Td>
<Td> <Td>
<Tooltip label={ isLoading ? undefined : 'Optimization' }> <Tooltip label={ isLoading ? undefined : 'Optimization' }>
......
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