Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine default and security score view on the Marketplace page #2031

Merged
merged 11 commits into from
Jun 25, 2024
5 changes: 0 additions & 5 deletions types/client/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ export enum ContractListTypes {
VERIFIED = 'Verified',
}

export enum MarketplaceDisplayType {
DEFAULT = 'default',
SCORES = 'scores',
}

export type MarketplaceAppSecurityReport = {
overallInfo: {
verifiedNumber: number;
Expand Down
61 changes: 45 additions & 16 deletions ui/marketplace/AppSecurityReport.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Box, Text, Link, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import { Box, Text, Link, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure, chakra, Flex, Divider, Icon } from '@chakra-ui/react';
import React from 'react';

import type { MarketplaceAppSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';

import config from 'configs/app';
// This icon doesn't work properly when it is in the sprite
// Probably because of the gradient
// eslint-disable-next-line no-restricted-imports
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
import { apos } from 'lib/html-entities';
import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg';
Expand All @@ -14,28 +19,33 @@ import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanRe
type Props = {
id: string;
securityReport?: MarketplaceAppSecurityReport;
showContractList: () => void;
showContractList: (id: string, type: ContractListTypes) => void;
isLoading?: boolean;
onlyIcon?: boolean;
source: 'Security view' | 'App modal' | 'App page';
source: 'Discovery view' | 'App modal' | 'App page';
className?: string;
popoverPlacement?: 'bottom-start' | 'bottom-end' | 'left';
}

const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, onlyIcon, source }: Props) => {
const AppSecurityReport = ({
id, securityReport, showContractList, isLoading, onlyIcon, source, className, popoverPlacement = 'bottom-start',
}: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();

const handleButtonClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Security score', Info: id, Source: source });
onToggle();
}, [ id, source, onToggle ]);

const handleLinkClick = React.useCallback(() => {
const showAnalyzedContracts = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Analyzed contracts', Info: id, Source: 'Security score popup' });
showContractList();
}, [ id, showContractList ]);
showContractList(id, ContractListTypes.ANALYZED);
}, [ showContractList, id ]);

if (!securityReport && !isLoading) {
return null;
}
const showAllContracts = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Total contracts', Info: id, Source: 'Security score popup' });
showContractList(id, ContractListTypes.ALL);
}, [ showContractList, id ]);

const {
securityScore = 0,
Expand All @@ -44,23 +54,43 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
totalIssues = 0,
} = securityReport?.overallInfo || {};

if ((!securityReport || !securityScore) && !isLoading) {
return null;
}

return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<Popover isOpen={ isOpen } onClose={ onClose } placement={ popoverPlacement } isLazy>
<PopoverTrigger>
<SolidityscanReportButton
score={ securityScore }
isLoading={ isLoading }
onClick={ handleButtonClick }
isActive={ isOpen }
onlyIcon={ onlyIcon }
label="The security score is based on analysis of a DApp's smart contracts."
label={ <>The security score is based on analysis<br/>of a DApp{ apos }s smart contracts.</> }
className={ className }
/>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Text fontWeight="500" fontSize="xs" mb={ 2 } variant="secondary">Smart contracts info</Text>
<Flex alignItems="center" justifyContent="space-between" h="32px">
<Flex alignItems="center">
maxaleks marked this conversation as resolved.
Show resolved Hide resolved
<IconSvg name="contracts_verified" boxSize={ 5 } color="green.500" mr={ 1 }/>
<Text>Verified contracts</Text>
</Flex>
<Link fontSize="sm" fontWeight="500" onClick={ showAllContracts }>
{ securityReport?.overallInfo.verifiedNumber ?? 0 } of { securityReport?.overallInfo.totalContractsNumber ?? 0 }
</Link>
</Flex>
<Divider my={ 3 }/>
<Box mb={ 5 }>
{ 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 by { ' ' }
<Box>
<Icon as={ solidityScanIcon } mr={ 1 } w="23px" h="20px" display="inline-block" verticalAlign="middle"/>
<Text fontWeight={ 600 } display="inline-block">SolidityScan</Text>
</Box>
</Box>
<SolidityscanReportScore score={ securityScore } mb={ 5 }/>
{ issueSeverityDistribution && totalIssues > 0 && (
Expand All @@ -69,14 +99,13 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
<SolidityscanReportDetails vulnerabilities={ issueSeverityDistribution } vulnerabilitiesCount={ totalIssues }/>
</Box>
) }
<Link onClick={ handleLinkClick } display="inline-flex" alignItems="center">
<Link onClick={ showAnalyzedContracts } display="inline-flex" alignItems="center">
Analyzed contracts
<IconSvg name="arrows/north-east" boxSize={ 5 } color="gray.400"/>
</Link>
</PopoverBody>
</PopoverContent>
</Popover>
);
};

export default AppSecurityReport;
export default chakra(AppSecurityReport);
66 changes: 0 additions & 66 deletions ui/marketplace/ContractListButton.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion ui/marketplace/ContractListModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const ContractListModal = ({ onClose, onBack, type, contracts }: Props) => {
switch (type) {
default:
case ContractListTypes.ALL:
return contracts;
return contracts.sort((a) => a.isVerified ? -1 : 1);
case ContractListTypes.ANALYZED:
return contracts
.filter((contract) => Boolean(contract.solidityScanReport))
Expand Down
12 changes: 10 additions & 2 deletions ui/marketplace/ContractSecurityReport.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Box, Text, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure } from '@chakra-ui/react';
import { Box, Text, Popover, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure, Icon } from '@chakra-ui/react';
import React from 'react';

import type { SolidityscanReport } from 'types/api/contract';

import config from 'configs/app';
// This icon doesn't work properly when it is in the sprite
// Probably because of the gradient
// eslint-disable-next-line no-restricted-imports
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
import * as mixpanel from 'lib/mixpanel/index';
import LinkExternal from 'ui/shared/links/LinkExternal';
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
Expand Down Expand Up @@ -46,7 +50,11 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<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 by { ' ' }
<Box>
<Icon as={ solidityScanIcon } mr={ 1 } w="23px" h="20px" display="inline-block" verticalAlign="middle"/>
<Text fontWeight={ 600 } display="inline-block">SolidityScan</Text>
</Box>
</Box>
<SolidityscanReportScore score={ parseFloat(securityScore) } mb={ 5 }/>
{ issueSeverityDistribution && totalIssues > 0 && (
Expand Down
Loading
Loading