mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
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
This commit is contained in:
@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development
|
||||
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
|
||||
|
||||
# Instance ENVs
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none
|
||||
|
||||
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
|
||||
NEXT_PUBLIC_API_BASE_PATH=/
|
||||
NEXT_PUBLIC_API_HOST=zksync.blockscout.com
|
||||
|
||||
@@ -99,6 +99,13 @@ export const withChangedByteCode: SmartContract = {
|
||||
is_blueprint: true,
|
||||
};
|
||||
|
||||
export const zkSync: SmartContract = {
|
||||
...verified,
|
||||
zk_compiler_version: 'v1.2.5',
|
||||
optimization_enabled: true,
|
||||
optimization_runs: 's',
|
||||
};
|
||||
|
||||
export const nonVerified: SmartContract = {
|
||||
is_verified: false,
|
||||
is_blueprint: false,
|
||||
|
||||
@@ -37,6 +37,7 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
|
||||
zkSyncRollup: [
|
||||
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkSync' ],
|
||||
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
|
||||
[ 'NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS', 'none' ],
|
||||
],
|
||||
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/"}]' ],
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface SmartContract {
|
||||
compiler_version: string | null;
|
||||
evm_version: string | null;
|
||||
optimization_enabled: boolean | null;
|
||||
optimization_runs: number | null;
|
||||
optimization_runs: number | string | null;
|
||||
name: string | null;
|
||||
verified_at: string | null;
|
||||
is_blueprint: boolean | null;
|
||||
@@ -57,6 +57,7 @@ export interface SmartContract {
|
||||
language: string | null;
|
||||
license_type: SmartContractLicenseType | null;
|
||||
certified?: boolean;
|
||||
zk_compiler_version?: string;
|
||||
}
|
||||
|
||||
export type SmartContractDecodedConstructorArg = [
|
||||
@@ -86,6 +87,8 @@ export interface SmartContractVerificationConfigRaw {
|
||||
vyper_evm_versions: Array<string>;
|
||||
is_rust_verifier_microservice_enabled: boolean;
|
||||
license_types: Record<SmartContractLicenseType, number>;
|
||||
zk_compiler_versions?: Array<string>;
|
||||
zk_optimization_modes?: Array<string>;
|
||||
}
|
||||
|
||||
export type SmartContractVerificationResponse = {
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface VerifiedContract {
|
||||
verified_at: string;
|
||||
market_cap: string | null;
|
||||
license_type: SmartContractLicenseType | null;
|
||||
zk_compiler_version?: string;
|
||||
}
|
||||
|
||||
export interface VerifiedContractsResponse {
|
||||
|
||||
@@ -126,6 +126,14 @@ test('non verified', async({ render, mockApiResponse }) => {
|
||||
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.beforeEach(async({ mockEnvs }) => {
|
||||
|
||||
@@ -60,6 +60,8 @@ const InfoItem = chakra(({ label, content, hint, className, isLoading }: InfoIte
|
||||
</GridItem>
|
||||
));
|
||||
|
||||
const rollupFeature = config.features.rollup;
|
||||
|
||||
const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
|
||||
const [ isChangedBytecodeSocket, setIsChangedBytecodeSocket ] = React.useState<boolean>();
|
||||
|
||||
@@ -266,6 +268,7 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
|
||||
<Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }>
|
||||
{ data.name && <InfoItem label="Contract name" content={ contractNameWithCertifiedIcon } 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 }/> }
|
||||
{ licenseLink && (
|
||||
<InfoItem
|
||||
@@ -277,8 +280,13 @@ const ContractCode = ({ addressHash, contractQuery, channel }: Props) => {
|
||||
) }
|
||||
{ typeof data.optimization_enabled === 'boolean' &&
|
||||
<InfoItem label="Optimization enabled" content={ data.optimization_enabled ? 'true' : 'false' } isLoading={ isPlaceholderData }/> }
|
||||
{ data.optimization_runs !== null &&
|
||||
<InfoItem label="Optimization runs" content={ String(data.optimization_runs) } isLoading={ isPlaceholderData }/> }
|
||||
{ data.optimization_runs !== null && (
|
||||
<InfoItem
|
||||
label={ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' ? 'Optimization mode' : 'Optimization runs' }
|
||||
content={ String(data.optimization_runs) }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
) }
|
||||
{ data.verified_at &&
|
||||
<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 }/> }
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { SmartContractVerificationConfig } from 'types/client/contract';
|
||||
|
||||
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
|
||||
import * as socketServer from 'playwright/fixtures/socketServer';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
@@ -215,3 +216,17 @@ test('solidity-foundry method', async({ render, page }) => {
|
||||
|
||||
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 {
|
||||
const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Props) => {
|
||||
const formApi = useForm<FormFields>({
|
||||
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 submitPromiseResolver = React.useRef<(value: unknown) => void>();
|
||||
|
||||
@@ -4,14 +4,15 @@ import React from 'react';
|
||||
interface Props {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
disableScroll?: boolean;
|
||||
}
|
||||
|
||||
const ContractVerificationMethod = ({ title, children }: Props) => {
|
||||
const ContractVerificationMethod = ({ title, children, disableScroll }: Props) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, []);
|
||||
!disableScroll && ref.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [ disableScroll ]);
|
||||
|
||||
return (
|
||||
<section ref={ ref }>
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
@@ -51,6 +51,7 @@ const ContractVerificationFieldMethod = ({ control, isDisabled, methods }: Props
|
||||
isDisabled={ isDisabled }
|
||||
isRequired
|
||||
isAsync={ false }
|
||||
isReadOnly={ options.length === 1 }
|
||||
/>
|
||||
);
|
||||
}, [ isDisabled, isMobile, options ]);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
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);
|
||||
@@ -0,0 +1,78 @@
|
||||
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';
|
||||
|
||||
import type { SmartContractVerificationConfig } from 'types/client/contract';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
import ContractVerificationMethod from '../ContractVerificationMethod';
|
||||
import ContractVerificationFieldAutodetectArgs from '../fields/ContractVerificationFieldAutodetectArgs';
|
||||
import ContractVerificationFieldCompiler from '../fields/ContractVerificationFieldCompiler';
|
||||
import ContractVerificationFieldName from '../fields/ContractVerificationFieldName';
|
||||
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
|
||||
import ContractVerificationFieldZkCompiler from '../fields/ContractVerificationFieldZkCompiler';
|
||||
import ContractVerificationFieldZkOptimization from '../fields/ContractVerificationFieldZkOptimization';
|
||||
|
||||
const FILE_TYPES = [ '.json' as const ];
|
||||
const rollupFeature = config.features.rollup;
|
||||
|
||||
const ContractVerificationStandardInput = ({ config }: { config: SmartContractVerificationConfig }) => {
|
||||
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/> }
|
||||
<ContractVerificationFieldCompiler/>
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && <ContractVerificationFieldZkCompiler/> }
|
||||
<ContractVerificationFieldSources
|
||||
fileTypes={ FILE_TYPES }
|
||||
title="Standard Input JSON"
|
||||
hint="Upload the standard input JSON file created during contract compilation."
|
||||
required
|
||||
/>
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && <ContractVerificationFieldZkOptimization config={ config }/> }
|
||||
{ !config?.is_rust_verifier_microservice_enabled && <ContractVerificationFieldAutodetectArgs/> }
|
||||
</ContractVerificationMethod>
|
||||
);
|
||||
|
||||
@@ -44,6 +44,20 @@ export interface FormFieldsStandardInput {
|
||||
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 {
|
||||
address: string;
|
||||
method: MethodOption;
|
||||
@@ -93,5 +107,5 @@ export interface FormFieldsVyperStandardInput {
|
||||
license_type: LicenseOption | null;
|
||||
}
|
||||
|
||||
export type FormFields = FormFieldsFlattenSourceCode | FormFieldsStandardInput | FormFieldsSourcify |
|
||||
export type FormFields = FormFieldsFlattenSourceCode | FormFieldsStandardInput | FormFieldsStandardInputZk | FormFieldsSourcify |
|
||||
FormFieldsMultiPartFile | FormFieldsVyperContract | FormFieldsVyperMultiPartFile | FormFieldsVyperStandardInput;
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
FormFieldsMultiPartFile,
|
||||
FormFieldsSourcify,
|
||||
FormFieldsStandardInput,
|
||||
FormFieldsStandardInputZk,
|
||||
FormFieldsVyperContract,
|
||||
FormFieldsVyperMultiPartFile,
|
||||
FormFieldsVyperStandardInput,
|
||||
@@ -155,11 +156,18 @@ export const DEFAULT_VALUES: Record<SmartContractVerificationMethod, FormFields>
|
||||
};
|
||||
|
||||
export function getDefaultValues(
|
||||
method: SmartContractVerificationMethod,
|
||||
methodParam: SmartContractVerificationMethod | undefined,
|
||||
config: SmartContractVerificationConfig,
|
||||
hash: string | undefined,
|
||||
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 };
|
||||
|
||||
if ('evm_version' in defaultValues) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -223,7 +238,7 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] {
|
||||
}
|
||||
|
||||
case 'standard-input': {
|
||||
const _data = data as FormFieldsStandardInput;
|
||||
const _data = data as (FormFieldsStandardInput | FormFieldsStandardInputZk);
|
||||
|
||||
const body = new FormData();
|
||||
_data.compiler && body.set('compiler_version', _data.compiler.value);
|
||||
@@ -233,6 +248,15 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] {
|
||||
body.set('constructor_args', _data.constructor_args);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,14 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
|
||||
<Box color="text_secondary" wordBreak="break-all" whiteSpace="pre-wrap"> ({ data.compiler_version })</Box>
|
||||
</Skeleton>
|
||||
</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 }>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Optimization</Skeleton>
|
||||
{ data.optimization_enabled ?
|
||||
|
||||
@@ -70,6 +70,14 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
|
||||
</Tooltip>
|
||||
</Skeleton>
|
||||
</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>
|
||||
<Tooltip label={ isLoading ? undefined : 'Optimization' }>
|
||||
|
||||
Reference in New Issue
Block a user