Commit cb3408dc authored by Max Alekseenko's avatar Max Alekseenko

add security report type

parent 2f05b159
import type { SolidityscanReport } from 'types/api/contract';
export type MarketplaceAppPreview = {
id: string;
external?: boolean;
......@@ -25,7 +27,7 @@ export type MarketplaceAppOverview = MarketplaceAppPreview & MarketplaceAppSocia
}
export type MarketplaceAppWithSecurityReport = MarketplaceAppOverview & {
securityReport: any; // eslint-disable-line @typescript-eslint/no-explicit-any
securityReport?: MarketplaceAppSecurityReport;
}
export enum MarketplaceCategory {
......@@ -43,3 +45,30 @@ export enum MarketplaceDisplayType {
DEFAULT = 'default',
SCORES = 'scores',
}
export type MarketplaceAppSecurityReport = {
overallInfo: {
verifiedNumber: number;
totalContractsNumber: number;
solidityScanContractsNumber: number;
securityScore: number;
totalIssues?: number;
issueSeverityDistribution: SolidityscanReport['scan_report']['scan_summary']['issue_severity_distribution'];
};
contractsData: [
{
address: string;
isVerified: boolean;
solidityScanReport?: SolidityscanReport['scan_report'] & {
contractname: string;
} | null;
}
];
}
export type MarketplaceAppSecurityReportRaw = {
appName: string;
chainsData: {
[chainId: string]: MarketplaceAppSecurityReport;
};
}
import { Box, Text, Link } from '@chakra-ui/react';
import React from 'react';
import type { MarketplaceAppSecurityReport } from 'types/client/marketplace';
import config from 'configs/app';
import { apos } from 'lib/html-entities';
import * as mixpanel from 'lib/mixpanel/index';
......@@ -11,7 +13,7 @@ import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanRe
type Props = {
id: string;
securityReport?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
securityReport?: MarketplaceAppSecurityReport;
height?: string | undefined;
showContractList: () => void;
isLoading?: boolean;
......@@ -37,7 +39,7 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa
const {
securityScore = 0,
solidityScanContractsNumber = 0,
issueSeverityDistribution = {},
issueSeverityDistribution = {} as MarketplaceAppSecurityReport['overallInfo']['issueSeverityDistribution'],
totalIssues = 0,
} = securityReport?.overallInfo || {};
......
......@@ -25,7 +25,7 @@ const values = {
};
interface Props {
children: string;
children: string | number;
onClick: (event: MouseEvent) => void;
variant: ContractListButtonVariants;
isLoading?: boolean;
......
......@@ -4,6 +4,7 @@ import {
} from '@chakra-ui/react';
import React from 'react';
import type { MarketplaceAppSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';
import useIsMobile from 'lib/hooks/useIsMobile';
......@@ -15,7 +16,7 @@ import ContractSecurityReport from './ContractSecurityReport';
type Props = {
onClose: () => void;
type: ContractListTypes;
contracts: Array<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
contracts?: MarketplaceAppSecurityReport['contractsData'];
}
const titles = {
......@@ -28,6 +29,9 @@ const ContractListModal = ({ onClose, type, contracts }: Props) => {
const isMobile = useIsMobile();
const displayedContracts = React.useMemo(() => {
if (!contracts) {
return [];
}
switch (type) {
default:
case ContractListTypes.ALL:
......@@ -35,12 +39,18 @@ const ContractListModal = ({ onClose, type, contracts }: Props) => {
case ContractListTypes.ANALYZED:
return contracts
.filter((contract) => Boolean(contract.solidityScanReport))
.sort((a, b) => b.solidityScanReport.scan_summary.score_v2 - a.solidityScanReport.scan_summary.score_v2);
.sort((a, b) =>
(parseFloat(b.solidityScanReport?.scan_summary.score_v2 ?? '0')) - (parseFloat(a.solidityScanReport?.scan_summary.score_v2 ?? '0')),
);
case ContractListTypes.VERIFIED:
return contracts.filter((contract) => contract.isVerified);
}
}, [ contracts, type ]);
if (!contracts) {
return null;
}
return (
<Modal
isOpen={ Boolean(type) }
......
import { Box, Text } from '@chakra-ui/react';
import React from 'react';
import type { SolidityscanReport } from 'types/api/contract';
import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/LinkExternal';
......@@ -9,34 +11,36 @@ import SolidityscanReportDetails from 'ui/shared/solidityscanReport/Solidityscan
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
type Props = {
securityReport?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
securityReport?: SolidityscanReport['scan_report'] | null;
}
const ContractSecurityReport = ({ securityReport }: Props) => {
const handleClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Source: 'Analyzed contracts popup' });
}, [ ]);
if (!securityReport) {
return null;
}
const url = securityReport?.scanner_reference_url;
const {
scanner_reference_url: url,
scan_summary: {
score_v2: securityScore,
issue_severity_distribution: issueSeverityDistribution,
},
} = securityReport;
} = securityReport.scan_summary;
const totalIssues = Object.values(issueSeverityDistribution as Record<string, number>).reduce((acc, val) => acc + val, 0);
const handleClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Source: 'Analyzed contracts popup' });
}, [ ]);
return (
<SolidityscanReportButton
score={ securityScore }
score={ parseFloat(securityScore) }
onClick={ handleClick }
popoverContent={ (
<>
<Box mb={ 5 }>
The security score was derived from evaluating the smart contracts of a protocol on the { config.chain.name } network.
</Box>
<SolidityscanReportScore score={ securityScore }/>
<SolidityscanReportScore score={ parseFloat(securityScore) }/>
{ issueSeverityDistribution && totalIssues > 0 && (
<Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Threat score & vulnerabilities</Text>
......
import { chakra, Flex, Tooltip, Skeleton, useBoolean, Box } from '@chakra-ui/react';
import React from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
import type { MarketplaceAppOverview, MarketplaceAppSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';
import { route } from 'nextjs-routes';
......@@ -22,7 +22,7 @@ type Props = {
data: MarketplaceAppOverview | undefined;
isLoading: boolean;
isWalletConnected: boolean;
securityReport?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
securityReport?: MarketplaceAppSecurityReport;
}
const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityReport }: Props) => {
......
......@@ -2,7 +2,7 @@ import { Flex, IconButton, Text } from '@chakra-ui/react';
import React from 'react';
import type { MouseEvent } from 'react';
import type { MarketplaceAppPreview } from 'types/client/marketplace';
import type { MarketplaceAppWithSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';
import * as mixpanel from 'lib/mixpanel/index';
......@@ -15,7 +15,7 @@ import AppLink from './AppLink';
import MoreInfoButton from './MoreInfoButton';
type Props = {
app: MarketplaceAppPreview & { securityReport?: any }; // eslint-disable-line @typescript-eslint/no-explicit-any
app: MarketplaceAppWithSecurityReport;
onInfoClick: (id: string) => void;
isFavorite: boolean;
onFavoriteClick: (id: string, isFavorite: boolean, source: 'Security view') => void;
......@@ -110,14 +110,14 @@ const ListItem = ({ app, onInfoClick, isFavorite, onFavoriteClick, isLoading, on
variant={ ContractListButtonVariants.ALL_CONTRACTS }
isLoading={ isLoading }
>
{ securityReport?.overallInfo.totalContractsNumber }
{ securityReport?.overallInfo.totalContractsNumber ?? 0 }
</ContractListButton>
<ContractListButton
onClick={ showVerifiedContracts }
variant={ ContractListButtonVariants.VERIFIED_CONTRACTS }
isLoading={ isLoading }
>
{ securityReport?.overallInfo.verifiedNumber }
{ securityReport?.overallInfo.verifiedNumber ?? 0 }
</ContractListButton>
</>
) : (
......
......@@ -2,14 +2,14 @@ import { Table as ChakraTable, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react';
import type { MouseEvent } from 'react';
import type { MarketplaceAppPreview, ContractListTypes } from 'types/client/marketplace';
import type { MarketplaceAppWithSecurityReport, ContractListTypes } from 'types/client/marketplace';
import { default as Thead } from 'ui/shared/TheadSticky';
import TableItem from './TableItem';
type Props = {
apps: Array<MarketplaceAppPreview>;
apps: Array<MarketplaceAppWithSecurityReport>;
isLoading?: boolean;
favoriteApps: Array<string>;
onFavoriteClick: (id: string, isFavorite: boolean, source: 'Security view') => void;
......
......@@ -2,7 +2,7 @@ import { Td, Tr, IconButton, Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
import type { MouseEvent } from 'react';
import type { MarketplaceAppPreview } from 'types/client/marketplace';
import type { MarketplaceAppWithSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';
import * as mixpanel from 'lib/mixpanel/index';
......@@ -14,7 +14,7 @@ import AppLink from './AppLink';
import MoreInfoButton from './MoreInfoButton';
type Props = {
app: MarketplaceAppPreview & { securityReport?: any }; // eslint-disable-line @typescript-eslint/no-explicit-any
app: MarketplaceAppWithSecurityReport;
isLoading?: boolean;
isFavorite: boolean;
onFavoriteClick: (id: string, isFavorite: boolean, source: 'Security view') => void;
......@@ -98,7 +98,7 @@ const TableItem = ({
variant={ ContractListButtonVariants.ALL_CONTRACTS }
isLoading={ isLoading }
>
{ securityReport?.overallInfo.totalContractsNumber }
{ securityReport?.overallInfo.totalContractsNumber ?? 0 }
</ContractListButton>
</Td>
<Td verticalAlign="middle">
......@@ -107,7 +107,7 @@ const TableItem = ({
variant={ ContractListButtonVariants.VERIFIED_CONTRACTS }
isLoading={ isLoading }
>
{ securityReport?.overallInfo.verifiedNumber }
{ securityReport?.overallInfo.verifiedNumber ?? 0 }
</ContractListButton>
</Td>
</>
......
import { useQuery } from '@tanstack/react-query';
import type { MarketplaceAppSecurityReport, MarketplaceAppSecurityReportRaw } from 'types/client/marketplace';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/hooks/useFetch';
......@@ -10,12 +12,12 @@ const securityReportsUrl = (feature.isEnabled && feature.securityReportsUrl) ||
export default function useSecurityReports() {
const apiFetch = useApiFetch();
return useQuery<unknown, ResourceError<unknown>, Record<string, any>>({ // eslint-disable-line @typescript-eslint/no-explicit-any
return useQuery<unknown, ResourceError<unknown>, Record<string, MarketplaceAppSecurityReport>>({
queryKey: [ 'marketplace-security-reports' ],
queryFn: async() => apiFetch(securityReportsUrl, undefined, { resource: 'marketplace-security-reports' }),
select: (data) => {
const securityReports: Record<string, any> = {}; // eslint-disable-line @typescript-eslint/no-explicit-any
(data as Array<any>).forEach((item) => { // eslint-disable-line @typescript-eslint/no-explicit-any
const securityReports: Record<string, MarketplaceAppSecurityReport> = {};
(data as Array<MarketplaceAppSecurityReportRaw>).forEach((item) => {
const report = item.chainsData[config.chain.id || ''];
if (report) {
const issues: Record<string, number> = report.overallInfo.issueSeverityDistribution;
......
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