Commit aabc034f authored by isstuev's avatar isstuev

soliditycheck report

parent 3d3b2515
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 3.6v4.812c0 6.447 5.414 8.703 6.79 9.17 1.377-.467 6.791-2.723 6.791-9.17V3.6H3ZM1.506 2.091A1.714 1.714 0 0 1 2.708 1.6h14.165c.448 0 .88.175 1.202.491.322.317.506.75.506 1.206v5.115c0 8.015-6.912 10.66-8.246 11.097a1.647 1.647 0 0 1-1.089 0C7.912 19.072 1 16.427 1 8.412V3.297c0-.455.184-.889.506-1.206Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.9 5.4a1.1 1.1 0 0 0-1.1 1.1v3.8a1.1 1.1 0 0 0 2.2 0V6.5a1.1 1.1 0 0 0-1.1-1.1Zm1.1 8.343a1.1 1.1 0 1 1-2.2 0 1.1 1.1 0 0 1 2.2 0Z" fill="currentColor"/>
</svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 8.412V3.6h13.581v4.812c0 6.447-5.414 8.703-6.79 9.17C8.414 17.115 3 14.86 3 8.412ZM2.708 1.6c-.448 0-.88.175-1.202.491-.322.317-.506.75-.506 1.206v5.115c0 8.015 6.912 10.66 8.246 11.097.352.123.737.123 1.089 0 1.334-.437 8.246-3.082 8.246-11.097V3.297c0-.455-.184-.889-.506-1.206a1.714 1.714 0 0 0-1.202-.491H2.708ZM14.37 8.208a1 1 0 1 0-1.369-1.458L8.49 10.986 6.58 9.191a1 1 0 1 0-1.37 1.457l2.594 2.44a1 1 0 0 0 1.37 0l5.196-4.88Z" fill="currentColor"/>
</svg>
...@@ -31,7 +31,7 @@ import type { AddressesResponse } from 'types/api/addresses'; ...@@ -31,7 +31,7 @@ import type { AddressesResponse } from 'types/api/addresses';
import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters, BlockWithdrawalsResponse } from 'types/api/block'; import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters, BlockWithdrawalsResponse } from 'types/api/block';
import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts'; import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts';
import type { BackendVersionConfig } from 'types/api/configs'; import type { BackendVersionConfig } from 'types/api/configs';
import type { SmartContract, SmartContractReadMethod, SmartContractWriteMethod, SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContract, SmartContractReadMethod, SmartContractWriteMethod, SmartContractVerificationConfig, SolidityscanReport } from 'types/api/contract';
import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts'; import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts';
import type { IndexingStatus } from 'types/api/indexingStatus'; import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
...@@ -343,6 +343,10 @@ export const RESOURCES = { ...@@ -343,6 +343,10 @@ export const RESOURCES = {
path: '/api/v2/smart-contracts/:hash/verification/via/:method', path: '/api/v2/smart-contracts/:hash/verification/via/:method',
pathParams: [ 'hash' as const, 'method' as const ], pathParams: [ 'hash' as const, 'method' as const ],
}, },
contract_solidityscan_report: {
path: '/api/v2/smart-contracts/:hash/solidityscan-report',
pathParams: [ 'hash' as const ],
},
verified_contracts: { verified_contracts: {
path: '/api/v2/smart-contracts', path: '/api/v2/smart-contracts',
...@@ -659,6 +663,7 @@ Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> : ...@@ -659,6 +663,7 @@ Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> :
Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> : Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> :
Q extends 'contract_methods_write' ? Array<SmartContractWriteMethod> : Q extends 'contract_methods_write' ? Array<SmartContractWriteMethod> :
Q extends 'contract_methods_write_proxy' ? Array<SmartContractWriteMethod> : Q extends 'contract_methods_write_proxy' ? Array<SmartContractWriteMethod> :
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' ? VisualizedContract : Q extends 'visualize_sol2uml' ? VisualizedContract :
......
export const solidityscanReport = {
scan_report: {
scan_status: 'scan_done',
scan_summary: {
issue_severity_distribution: {
critical: 0,
gas: 1,
high: 0,
informational: 0,
low: 2,
medium: 0,
},
lines_analyzed_count: 18,
scan_time_taken: 1,
score: '3.61',
score_v2: '72.22',
threat_score: '94.74',
},
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
},
};
import type { SmartContract } from 'types/api/contract'; import type { SmartContract, SolidityscanReport } from 'types/api/contract';
import type { VerifiedContract } from 'types/api/contracts'; import type { VerifiedContract } from 'types/api/contracts';
import { ADDRESS_PARAMS } from './addressParams'; import { ADDRESS_PARAMS } from './addressParams';
...@@ -53,3 +53,25 @@ export const VERIFIED_CONTRACT_INFO: VerifiedContract = { ...@@ -53,3 +53,25 @@ export const VERIFIED_CONTRACT_INFO: VerifiedContract = {
tx_count: 565058, tx_count: 565058,
verified_at: '2023-04-10T13:16:33.884921Z', verified_at: '2023-04-10T13:16:33.884921Z',
}; };
export const SOLIDITYSCAN_REPORT: SolidityscanReport = {
scan_report: {
scan_status: 'scan_done',
scan_summary: {
issue_severity_distribution: {
critical: 0,
gas: 1,
high: 0,
informational: 0,
low: 2,
medium: 0,
},
lines_analyzed_count: 18,
scan_time_taken: 1,
score: '3.61',
score_v2: '72.22',
threat_score: '94.74',
},
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
},
};
...@@ -156,3 +156,25 @@ export interface SmartContractVerificationError { ...@@ -156,3 +156,25 @@ export interface SmartContractVerificationError {
constructor_arguments?: Array<string>; constructor_arguments?: Array<string>;
name?: Array<string>; name?: Array<string>;
} }
export type SolidityscanReport = {
scan_report: {
scan_status: string;
scan_summary: {
issue_severity_distribution: {
critical: number;
gas: number;
high: number;
informational: number;
low: number;
medium: number;
};
lines_analyzed_count: number;
scan_time_taken: number;
score: string;
score_v2: string;
threat_score: string;
};
scanner_reference_url: string;
};
}
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { solidityscanReport as solidityscanReportMock } from 'mocks/contract/solidityscanReport';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import SolidityscanReport from './SolidityscanReport';
const addressHash = 'hash';
const REPORT_API_URL = buildApiUrl('contract_solidityscan_report', { hash: addressHash });
test('base view +@dark-mode +@mobile', async({ mount, page }) => {
await page.route(REPORT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(solidityscanReportMock),
}));
const component = await mount(
<TestApp>
<SolidityscanReport hash={ addressHash }/>
</TestApp>,
);
await component.getByLabel('SolidityScan score').click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 400, height: 500 } });
});
import {
Box,
Flex,
Text,
Grid,
Button,
Icon,
chakra,
Popover,
PopoverTrigger,
PopoverBody,
PopoverContent,
useDisclosure,
Skeleton,
Center,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react';
import { SolidityscanReport } from 'types/api/contract';
import scoreNotOkIcon from 'icons/score/score-not-ok.svg';
import scoreOkIcon from 'icons/score/score-ok.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { SOLIDITYSCAN_REPORT } from 'stubs/contract';
import LinkExternal from 'ui/shared/LinkExternal';
type DistributionItem = {
id: keyof SolidityscanReport['scan_report']['scan_summary']['issue_severity_distribution'];
name: string;
color: string;
}
const DISTRIBUTION_ITEMS: Array<DistributionItem> = [
{ id: 'critical', name: 'Critical', color: '#891F11' },
{ id: 'high', name: 'High', color: '#EC672C' },
{ id: 'medium', name: 'Medium', color: '#FBE74D' },
{ id: 'low', name: 'Low', color: '#68C88E' },
{ id: 'informational', name: 'Informational', color: '#A3AEBE' },
{ id: 'gas', name: 'Gas', color: '#A47585' },
];
interface Props {
className?: string;
hash: string;
}
const SolidityscanReport = ({ className, hash }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const { data, isPlaceholderData, isError } = useApiQuery('contract_solidityscan_report', {
pathParams: { hash },
queryOptions: {
enabled: Boolean(hash),
placeholderData: SOLIDITYSCAN_REPORT,
},
});
const score = Number(data?.scan_report.scan_summary.score_v2);
const bgBar = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const chartGrayColor = useColorModeValue('gray.100', 'gray.700');
const yetAnotherGrayColor = useColorModeValue('gray.400', 'gray.500');
const popoverBgColor = useColorModeValue('white', 'gray.900');
if (isError || !score) {
return null;
}
let scoreColor;
let scoreLevel;
if (score >= 80) {
scoreColor = 'green.600';
scoreLevel = 'GREAT';
} else if (score >= 30) {
scoreColor = 'orange.600';
scoreLevel = 'AVERAGE';
} else {
scoreColor = 'red.600';
scoreLevel = 'LOW';
}
const vulnerabilities = data?.scan_report.scan_summary.issue_severity_distribution;
const vulnerabilitiesCounts = vulnerabilities ? Object.values(vulnerabilities) : [];
const vulnerabilitiesCount = vulnerabilitiesCounts.reduce((acc, val) => acc + val, 0);
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<Skeleton isLoaded={ !isPlaceholderData }>
<Button
className={ className }
color={ scoreColor }
size="sm"
variant="outline"
colorScheme="gray"
onClick={ onToggle }
aria-label="SolidityScan score"
fontWeight={ 500 }
px={ 2 }
h="32px"
flexShrink={ 0 }
>
<Icon as={ score < 80 ? scoreNotOkIcon : scoreOkIcon } boxSize={ 5 } mr={ 1 }/>
{ score }
</Button>
</Skeleton>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Box mb={ 5 }>Contract analyzed for 140+ vulnerability patterns by SolidityScan</Box>
<Flex alignItems="center" mb={ 5 }>
<Box
w={ 12 }
h={ 12 }
bgGradient={ `conic-gradient(${ scoreColor } 0, ${ scoreColor } ${ score }%, ${ chartGrayColor } 0, ${ chartGrayColor } 100%)` }
borderRadius="24px"
position="relative"
mr={ 3 }
>
<Center position="absolute" w="38px" h="38px" top="5px" right="5px" bg={ popoverBgColor } borderRadius="20px">
<Icon as={ score < 80 ? scoreNotOkIcon : scoreOkIcon } boxSize={ 5 } color={ scoreColor }/>
</Center>
</Box>
<Box>
<Flex>
<Text color={ scoreColor } fontSize="lg" fontWeight={ 500 }>{ score }</Text>
<Text color={ yetAnotherGrayColor } fontSize="lg" fontWeight={ 500 } whiteSpace="pre"> / 100</Text>
</Flex>
<Text color={ scoreColor } fontWeight={ 500 }>Security score is { scoreLevel }</Text>
</Box>
</Flex>
{ vulnerabilities && vulnerabilitiesCount && (
<Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Vulnerabilities distribution</Text>
<Grid templateColumns="20px 1fr 100px" alignItems="center" rowGap={ 2 }>
{ DISTRIBUTION_ITEMS.map(item => (
<>
<Box w={ 3 } h={ 3 } bg={ item.color } borderRadius="6px" mr={ 2 }></Box>
<Flex justifyContent="space-between" mr={ 3 }>
<Text>{ item.name }</Text>
<Text color={ vulnerabilities[item.id] > 0 ? 'text' : yetAnotherGrayColor }>{ vulnerabilities[item.id] }</Text>
</Flex>
<Box bg={ bgBar } h="10px" borderRadius="8px">
<Box bg={ item.color } w={ vulnerabilities[item.id] / vulnerabilitiesCount } h="10px" borderRadius="8px"/>
</Box>
</>
)) }
</Grid>
</Box>
) }
<LinkExternal href={ data?.scan_report.scanner_reference_url }>View full report</LinkExternal>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default chakra(SolidityscanReport);
import { Box, Flex, Icon } from '@chakra-ui/react'; import { Box, Flex, HStack, Icon } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -25,6 +25,7 @@ import AddressTxs from 'ui/address/AddressTxs'; ...@@ -25,6 +25,7 @@ import AddressTxs from 'ui/address/AddressTxs';
import AddressWithdrawals from 'ui/address/AddressWithdrawals'; import AddressWithdrawals from 'ui/address/AddressWithdrawals';
import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressQrCode from 'ui/address/details/AddressQrCode'; import AddressQrCode from 'ui/address/details/AddressQrCode';
import SolidityscanReport from 'ui/address/SolidityscanReport';
import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
...@@ -194,7 +195,9 @@ const AddressPageContent = () => { ...@@ -194,7 +195,9 @@ const AddressPageContent = () => {
) } ) }
<AddressQrCode address={{ hash }} isLoading={ isLoading }/> <AddressQrCode address={{ hash }} isLoading={ isLoading }/>
<AccountActionsMenu isLoading={ isLoading }/> <AccountActionsMenu isLoading={ isLoading }/>
<NetworkExplorers type="address" pathParam={ hash } ml="auto"/> <HStack ml="auto" gap={ 2 }/>
<SolidityscanReport hash={ hash }/>
<NetworkExplorers type="address" pathParam={ hash }/>
</Flex> </Flex>
); );
......
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