Commit b2175db8 authored by tom's avatar tom

alerts

parent 141f72bd
...@@ -15,6 +15,7 @@ export interface SmartContract { ...@@ -15,6 +15,7 @@ export interface SmartContract {
source_code: string | null; source_code: string | null;
constructor_args: string | null; constructor_args: string | null;
can_be_visualized_via_sol2uml: boolean | null; can_be_visualized_via_sol2uml: boolean | null;
is_vyper_contract: boolean | null;
} }
export interface SmartContractMethodBase { export interface SmartContractMethodBase {
......
import { Flex, Skeleton, Button, Grid, GridItem, Text } from '@chakra-ui/react'; import { Flex, Skeleton, Button, Grid, GridItem, Text, Alert, Link, chakra } from '@chakra-ui/react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import link from 'lib/link/link'; import link from 'lib/link/link';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import ExternalLink from 'ui/shared/ExternalLink';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
const DynamicContractSourceCode = dynamic( const DynamicContractSourceCode = dynamic(
...@@ -23,10 +27,11 @@ const InfoItem = ({ label, value }: { label: string; value: string }) => ( ...@@ -23,10 +27,11 @@ const InfoItem = ({ label, value }: { label: string; value: string }) => (
const ContractCode = () => { const ContractCode = () => {
const router = useRouter(); const router = useRouter();
const addressHash = router.query.id?.toString();
const { data, isLoading, isError } = useApiQuery('contract', { const { data, isLoading, isError } = useApiQuery('contract', {
pathParams: { id: router.query.id?.toString() }, pathParams: { id: addressHash },
queryOptions: { queryOptions: {
enabled: Boolean(router.query.id), enabled: Boolean(addressHash),
refetchOnMount: false, refetchOnMount: false,
}, },
}); });
...@@ -58,7 +63,7 @@ const ContractCode = () => { ...@@ -58,7 +63,7 @@ const ContractCode = () => {
ml="auto" ml="auto"
mr={ 3 } mr={ 3 }
as="a" as="a"
href={ link('address_contract_verification', { id: router.query.id?.toString() }) } href={ link('address_contract_verification', { id: addressHash }) }
> >
Verify & publish Verify & publish
</Button> </Button>
...@@ -66,6 +71,26 @@ const ContractCode = () => { ...@@ -66,6 +71,26 @@ const ContractCode = () => {
return ( return (
<> <>
<Flex flexDir="column" rowGap={ 2 } mb={ 6 }>
<Alert status="success">Contract Source Code Verified (Exact Match)</Alert>
<Alert status="warning" whiteSpace="pre-wrap" flexWrap="wrap">
<span>This contract has been partially verified via Sourcify. </span>
<ExternalLink href="https://repo.sourcify.dev/" title="View contract in Sourcify repository" fontSize="md"/>
</Alert>
<Alert status="warning">
Warning! Contract bytecode has been changed and does not match the verified one. Therefore, interaction with this smart contract may be risky.
</Alert>
<Alert status="warning" whiteSpace="pre-wrap" flexWrap="wrap">
<span>Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB </span>
<Address>
<AddressIcon address={{ hash: addressHash || '', is_contract: false }}/>
<AddressLink hash={ addressHash || '' } truncation="constant" ml={ 2 }/>
</Address>
<chakra.span mt={ 1 }>All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with </chakra.span>
<Link>Verify & Publish</Link>
<span> page</span>
</Alert>
</Flex>
{ data.is_verified && ( { data.is_verified && (
<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" value={ data.name }/> } { data.name && <InfoItem label="Contract name" value={ data.name }/> }
...@@ -87,7 +112,8 @@ const ContractCode = () => { ...@@ -87,7 +112,8 @@ const ContractCode = () => {
<DynamicContractSourceCode <DynamicContractSourceCode
data={ data.source_code } data={ data.source_code }
hasSol2Yml={ Boolean(data.can_be_visualized_via_sol2uml) } hasSol2Yml={ Boolean(data.can_be_visualized_via_sol2uml) }
address={ router.query.id?.toString() } address={ addressHash }
isViper={ Boolean(data.is_vyper_contract) }
/> />
) } ) }
{ data.abi && ( { data.abi && (
...@@ -102,6 +128,12 @@ const ContractCode = () => { ...@@ -102,6 +128,12 @@ const ContractCode = () => {
data={ data.creation_bytecode } data={ data.creation_bytecode }
title="Contract creation code" title="Contract creation code"
rightSlot={ data.is_verified ? null : verificationButton } rightSlot={ data.is_verified ? null : verificationButton }
beforeSlot={ (
<Alert status="info" whiteSpace="pre-wrap" mb={ 3 }>
Contracts that self destruct in their constructors have no contract code published and cannot be verified.
Displaying the init data provided of the creating transaction.
</Alert>
) }
/> />
) } ) }
{ data.deployed_bytecode && ( { data.deployed_bytecode && (
......
...@@ -9,13 +9,17 @@ interface Props { ...@@ -9,13 +9,17 @@ interface Props {
data: string; data: string;
hasSol2Yml: boolean; hasSol2Yml: boolean;
address?: string; address?: string;
isViper: boolean;
} }
const ContractSourceCode = ({ data, hasSol2Yml, address }: Props) => { const ContractSourceCode = ({ data, hasSol2Yml, address, isViper }: Props) => {
return ( return (
<Box> <Box>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }> <Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
<Text fontWeight={ 500 }>Contract source code</Text> <Text fontWeight={ 500 }>
<span>Contract source code</span>
<Text whiteSpace="pre" as="span" variant="secondary"> ({ isViper ? 'Vyper' : 'Solidity' })</Text>
</Text>
{ hasSol2Yml && address && ( { hasSol2Yml && address && (
<Tooltip label="Visualize contract code using Sol2Uml JS library"> <Tooltip label="Visualize contract code using Sol2Uml JS library">
<Link <Link
......
...@@ -2,7 +2,7 @@ import { Alert } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Alert } from '@chakra-ui/react';
import _capitalize from 'lodash/capitalize'; import _capitalize from 'lodash/capitalize';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { useSigner } from 'wagmi'; import { useAccount, useSigner } from 'wagmi';
import type { ContractMethodWriteResult } from './types'; import type { ContractMethodWriteResult } from './types';
import type { SmartContractWriteMethod } from 'types/api/contract'; import type { SmartContractWriteMethod } from 'types/api/contract';
...@@ -28,6 +28,7 @@ const ContractWrite = ({ isProxy }: Props) => { ...@@ -28,6 +28,7 @@ const ContractWrite = ({ isProxy }: Props) => {
const addressHash = router.query.id?.toString(); const addressHash = router.query.id?.toString();
const { data: signer } = useSigner(); const { data: signer } = useSigner();
const { isConnected } = useAccount();
const { data, isLoading, isError } = useApiQuery(isProxy ? 'contract_methods_write_proxy' : 'contract_methods_write', { const { data, isLoading, isError } = useApiQuery(isProxy ? 'contract_methods_write_proxy' : 'contract_methods_write', {
pathParams: { id: addressHash }, pathParams: { id: addressHash },
...@@ -40,6 +41,11 @@ const ContractWrite = ({ isProxy }: Props) => { ...@@ -40,6 +41,11 @@ const ContractWrite = ({ isProxy }: Props) => {
const _contract = isProxy ? proxy : contract; const _contract = isProxy ? proxy : contract;
const handleMethodFormSubmit = React.useCallback(async(item: SmartContractWriteMethod, args: Array<string | Array<string>>) => { const handleMethodFormSubmit = React.useCallback(async(item: SmartContractWriteMethod, args: Array<string | Array<string>>) => {
if (!isConnected) {
throw new Error('Wallet is not connected');
}
try { try {
if (!_contract) { if (!_contract) {
return; return;
...@@ -83,7 +89,7 @@ const ContractWrite = ({ isProxy }: Props) => { ...@@ -83,7 +89,7 @@ const ContractWrite = ({ isProxy }: Props) => {
} }
throw error; throw error;
} }
}, [ _contract, addressHash, signer ]); }, [ _contract, addressHash, isConnected, signer ]);
const renderResult = React.useCallback((item: SmartContractWriteMethod, result: ContractMethodWriteResult) => { const renderResult = React.useCallback((item: SmartContractWriteMethod, result: ContractMethodWriteResult) => {
if (!result || 'message' in result) { if (!result || 'message' in result) {
......
import { Link, Icon } from '@chakra-ui/react'; import { Link, Icon, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import arrowIcon from 'icons/arrows/north-east.svg'; import arrowIcon from 'icons/arrows/north-east.svg';
...@@ -6,15 +6,16 @@ import arrowIcon from 'icons/arrows/north-east.svg'; ...@@ -6,15 +6,16 @@ import arrowIcon from 'icons/arrows/north-east.svg';
interface Props { interface Props {
href: string; href: string;
title: string; title: string;
className?: string;
} }
const ExternalLink = ({ href, title }: Props) => { const ExternalLink = ({ href, title, className }: Props) => {
return ( return (
<Link fontSize="sm" display="inline-flex" alignItems="center" target="_blank" href={ href }> <Link className={ className } fontSize="sm" display="inline-flex" alignItems="center" target="_blank" href={ href }>
{ title } { title }
<Icon as={ arrowIcon } boxSize={ 4 }/> <Icon as={ arrowIcon } boxSize={ 4 }/>
</Link> </Link>
); );
}; };
export default React.memo(ExternalLink); export default React.memo(chakra(ExternalLink));
...@@ -8,10 +8,11 @@ interface Props { ...@@ -8,10 +8,11 @@ interface Props {
title?: string; title?: string;
className?: string; className?: string;
rightSlot?: React.ReactNode; rightSlot?: React.ReactNode;
beforeSlot?: React.ReactNode;
textareaMinHeight?: string; textareaMinHeight?: string;
} }
const RawDataSnippet = ({ data, className, title, rightSlot, textareaMinHeight }: Props) => { const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textareaMinHeight }: Props) => {
return ( return (
<Box className={ className }> <Box className={ className }>
<Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }> <Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }>
...@@ -19,6 +20,7 @@ const RawDataSnippet = ({ data, className, title, rightSlot, textareaMinHeight } ...@@ -19,6 +20,7 @@ const RawDataSnippet = ({ data, className, title, rightSlot, textareaMinHeight }
{ rightSlot } { rightSlot }
<CopyToClipboard text={ data }/> <CopyToClipboard text={ data }/>
</Flex> </Flex>
{ beforeSlot }
<Textarea <Textarea
variant="filledInactive" variant="filledInactive"
p={ 4 } p={ 4 }
......
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