Commit 6d17681f authored by Max Alekseenko's avatar Max Alekseenko

rework solidity report component

parent 0052c025
import { Box, Text, chakra, Icon } from '@chakra-ui/react'; import { Box, Text, chakra, Icon, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
// This icon doesn't work properly when it is in the sprite // This icon doesn't work properly when it is in the sprite
...@@ -18,6 +18,8 @@ interface Props { ...@@ -18,6 +18,8 @@ interface Props {
} }
const SolidityscanReport = ({ className, hash }: Props) => { const SolidityscanReport = ({ className, hash }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const { data, isPlaceholderData, isError } = useApiQuery('contract_solidityscan_report', { const { data, isPlaceholderData, isError } = useApiQuery('contract_solidityscan_report', {
pathParams: { hash }, pathParams: { hash },
queryOptions: { queryOptions: {
...@@ -37,18 +39,23 @@ const SolidityscanReport = ({ className, hash }: Props) => { ...@@ -37,18 +39,23 @@ const SolidityscanReport = ({ className, hash }: Props) => {
const vulnerabilitiesCount = vulnerabilitiesCounts.reduce((acc, val) => acc + val, 0); const vulnerabilitiesCount = vulnerabilitiesCounts.reduce((acc, val) => acc + val, 0);
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<SolidityscanReportButton <SolidityscanReportButton
className={ className } className={ className }
score={ score } score={ score }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
popoverContent={ ( onClick={ onToggle }
<> />
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Box mb={ 5 } lineHeight="25px"> <Box mb={ 5 } lineHeight="25px">
Contract analyzed for 140+ vulnerability patterns by Contract analyzed for 140+ vulnerability patterns by
<Icon as={ solidityScanIcon } mr={ 1 } ml="6px" w="23px" h="20px" display="inline-block" verticalAlign="middle"/> <Icon as={ solidityScanIcon } mr={ 1 } ml="6px" w="23px" h="20px" display="inline-block" verticalAlign="middle"/>
<Text fontWeight={ 600 } display="inline-block">SolidityScan</Text> <Text fontWeight={ 600 } display="inline-block">SolidityScan</Text>
</Box> </Box>
<SolidityscanReportScore score={ score }/> <SolidityscanReportScore score={ score } mb={ 5 }/>
{ vulnerabilities && vulnerabilitiesCount > 0 && ( { vulnerabilities && vulnerabilitiesCount > 0 && (
<Box mb={ 5 }> <Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Vulnerabilities distribution</Text> <Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Vulnerabilities distribution</Text>
...@@ -56,9 +63,9 @@ const SolidityscanReport = ({ className, hash }: Props) => { ...@@ -56,9 +63,9 @@ const SolidityscanReport = ({ className, hash }: Props) => {
</Box> </Box>
) } ) }
<LinkExternal href={ data?.scan_report.scanner_reference_url }>View full report</LinkExternal> <LinkExternal href={ data?.scan_report.scanner_reference_url }>View full report</LinkExternal>
</> </PopoverBody>
) } </PopoverContent>
/> </Popover>
); );
}; };
......
import { Box, Text, Link } from '@chakra-ui/react'; import { Box, Text, Link, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { MarketplaceAppSecurityReport } from 'types/client/marketplace'; import type { MarketplaceAppSecurityReport } from 'types/client/marketplace';
...@@ -22,10 +22,12 @@ type Props = { ...@@ -22,10 +22,12 @@ type Props = {
} }
const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoading, onlyIcon, source }: Props) => { const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoading, onlyIcon, source }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const handleButtonClick = React.useCallback(() => { const handleButtonClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Info: id, Source: source }); mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Info: id, Source: source });
}, [ id, source ]); onToggle();
}, [ id, source, onToggle ]);
const handleLinkClick = React.useCallback(() => { const handleLinkClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Analyzed contracts', Info: id, Source: 'Security score popup' }); mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Analyzed contracts', Info: id, Source: 'Security score popup' });
...@@ -44,19 +46,23 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa ...@@ -44,19 +46,23 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa
} = securityReport?.overallInfo || {}; } = securityReport?.overallInfo || {};
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<SolidityscanReportButton <SolidityscanReportButton
score={ securityScore }
isLoading={ isLoading } isLoading={ isLoading }
onClick={ handleButtonClick }
height={ height } height={ height }
score={ securityScore }
onlyIcon={ onlyIcon } onlyIcon={ onlyIcon }
onClick={ handleButtonClick } />
popoverContent={ ( </PopoverTrigger>
<> <PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Box mb={ 5 }> <Box mb={ 5 }>
{ solidityScanContractsNumber } smart contract{ solidityScanContractsNumber === 1 ? ' was' : 's were' } evaluated to determine { solidityScanContractsNumber } smart contract{ solidityScanContractsNumber === 1 ? ' was' : 's were' } evaluated to determine
this protocol{ apos }s overall security score on the { config.chain.name } network. this protocol{ apos }s overall security score on the { config.chain.name } network.
</Box> </Box>
<SolidityscanReportScore score={ securityScore }/> <SolidityscanReportScore score={ securityScore } mb={ 5 }/>
{ issueSeverityDistribution && totalIssues > 0 && ( { issueSeverityDistribution && totalIssues > 0 && (
<Box mb={ 5 }> <Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Threat score & vulnerabilities</Text> <Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Threat score & vulnerabilities</Text>
...@@ -67,9 +73,9 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa ...@@ -67,9 +73,9 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa
Analyzed contracts Analyzed contracts
<IconSvg name="arrows/north-east" boxSize={ 5 } color="gray.400"/> <IconSvg name="arrows/north-east" boxSize={ 5 } color="gray.400"/>
</Link> </Link>
</> </PopoverBody>
) } </PopoverContent>
/> </Popover>
); );
}; };
......
import { Box, Text } from '@chakra-ui/react'; import { Box, Text, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SolidityscanReport } from 'types/api/contract'; import type { SolidityscanReport } from 'types/api/contract';
...@@ -15,9 +15,12 @@ type Props = { ...@@ -15,9 +15,12 @@ type Props = {
} }
const ContractSecurityReport = ({ securityReport }: Props) => { const ContractSecurityReport = ({ securityReport }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const handleClick = React.useCallback(() => { const handleClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Source: 'Analyzed contracts popup' }); mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Source: 'Analyzed contracts popup' });
}, [ ]); onToggle();
}, [ onToggle ]);
if (!securityReport) { if (!securityReport) {
return null; return null;
...@@ -32,15 +35,19 @@ const ContractSecurityReport = ({ securityReport }: Props) => { ...@@ -32,15 +35,19 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
const totalIssues = Object.values(issueSeverityDistribution as Record<string, number>).reduce((acc, val) => acc + val, 0); const totalIssues = Object.values(issueSeverityDistribution as Record<string, number>).reduce((acc, val) => acc + val, 0);
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<SolidityscanReportButton <SolidityscanReportButton
score={ parseFloat(securityScore) } score={ parseFloat(securityScore) }
onClick={ handleClick } onClick={ handleClick }
popoverContent={ ( />
<> </PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Box mb={ 5 }> <Box mb={ 5 }>
The security score was derived from evaluating the smart contracts of a protocol on the { config.chain.name } network. The security score was derived from evaluating the smart contracts of a protocol on the { config.chain.name } network.
</Box> </Box>
<SolidityscanReportScore score={ parseFloat(securityScore) }/> <SolidityscanReportScore score={ parseFloat(securityScore) } mb={ 5 }/>
{ issueSeverityDistribution && totalIssues > 0 && ( { issueSeverityDistribution && totalIssues > 0 && (
<Box mb={ 5 }> <Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Threat score & vulnerabilities</Text> <Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Threat score & vulnerabilities</Text>
...@@ -48,9 +55,9 @@ const ContractSecurityReport = ({ securityReport }: Props) => { ...@@ -48,9 +55,9 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
</Box> </Box>
) } ) }
<LinkExternal href={ url }>View full report</LinkExternal> <LinkExternal href={ url }>View full report</LinkExternal>
</> </PopoverBody>
) } </PopoverContent>
/> </Popover>
); );
}; };
......
import { import { Button, Skeleton } from '@chakra-ui/react';
Button,
chakra,
Popover,
PopoverTrigger,
PopoverBody,
PopoverContent,
useDisclosure,
Skeleton,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -17,33 +8,28 @@ import useScoreLevelAndColor from './useScoreLevelAndColor'; ...@@ -17,33 +8,28 @@ import useScoreLevelAndColor from './useScoreLevelAndColor';
interface Props { interface Props {
className?: string; className?: string;
score: number; score: number;
popoverContent?: React.ReactNode;
isLoading?: boolean; isLoading?: boolean;
height?: string; height?: string;
onlyIcon?: boolean; onlyIcon?: boolean;
onClick?: () => void; onClick?: () => void;
} }
const SolidityscanReportButton = ({ className, score, popoverContent, isLoading, height = '32px', onlyIcon, onClick }: Props) => { const SolidityscanReportButton = (
const { isOpen, onToggle, onClose } = useDisclosure(); { className, score, isLoading, height = '32px', onlyIcon, onClick }: Props,
ref: React.ForwardedRef<HTMLButtonElement>,
) => {
const { scoreColor } = useScoreLevelAndColor(score); const { scoreColor } = useScoreLevelAndColor(score);
const handleClick = React.useCallback(() => {
onClick?.();
onToggle();
}, [ onClick, onToggle ]);
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<Skeleton isLoaded={ !isLoading } borderRadius="base"> <Skeleton isLoaded={ !isLoading } borderRadius="base">
<Button <Button
ref={ ref }
className={ className } className={ className }
color={ scoreColor } color={ scoreColor }
size="sm" size="sm"
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ handleClick } onClick={ onClick }
aria-label="SolidityScan score" aria-label="SolidityScan score"
fontWeight={ 500 } fontWeight={ 500 }
px="6px" px="6px"
...@@ -54,14 +40,7 @@ const SolidityscanReportButton = ({ className, score, popoverContent, isLoading, ...@@ -54,14 +40,7 @@ const SolidityscanReportButton = ({ className, score, popoverContent, isLoading,
{ onlyIcon ? null : score } { onlyIcon ? null : score }
</Button> </Button>
</Skeleton> </Skeleton>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
{ popoverContent }
</PopoverBody>
</PopoverContent>
</Popover>
); );
}; };
export default chakra(SolidityscanReportButton); export default React.forwardRef(SolidityscanReportButton);
...@@ -6,10 +6,11 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -6,10 +6,11 @@ import IconSvg from 'ui/shared/IconSvg';
import useScoreLevelAndColor from './useScoreLevelAndColor'; import useScoreLevelAndColor from './useScoreLevelAndColor';
interface Props { interface Props {
className?: string;
score: number; score: number;
} }
const SolidityscanReportScore = ({ score }: Props) => { const SolidityscanReportScore = ({ className, score }: Props) => {
const { scoreLevel, scoreColor } = useScoreLevelAndColor(score); const { scoreLevel, scoreColor } = useScoreLevelAndColor(score);
const chartGrayColor = useColorModeValue('gray.100', 'gray.700'); const chartGrayColor = useColorModeValue('gray.100', 'gray.700');
...@@ -17,7 +18,7 @@ const SolidityscanReportScore = ({ score }: Props) => { ...@@ -17,7 +18,7 @@ const SolidityscanReportScore = ({ score }: Props) => {
const popoverBgColor = useColorModeValue('white', 'gray.900'); const popoverBgColor = useColorModeValue('white', 'gray.900');
return ( return (
<Flex alignItems="center" mb={ 5 }> <Flex className={ className } alignItems="center">
<Box <Box
w={ 12 } w={ 12 }
h={ 12 } h={ 12 }
......
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