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

Blockaid banners implementation #6585

Merged
merged 41 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2ffc052
Tests should use find by not just snapshots
segun Jun 8, 2023
fcde72c
BlockaidBanner implementation
segun Jun 7, 2023
25b21dd
Blockaid banner tests
segun Jun 12, 2023
aa72c6a
add a flag type
segun Jun 12, 2023
d64451e
Add attribution line
segun Jun 12, 2023
38f036c
add flag type to stories
segun Jun 12, 2023
c5a3321
update banner snapshot
segun Jun 19, 2023
31d6053
Organized import.
segun Jun 23, 2023
8ba58e4
Fix PR Comments
segun Jun 26, 2023
5c3b448
fix import path
segun Jun 27, 2023
889b02e
house cleaning
segun Jun 28, 2023
f793985
Add horizontal header alignment
segun Jul 18, 2023
3a18103
fix imports
segun Jul 18, 2023
ea86998
Update snapshots
segun Jul 18, 2023
f16c834
Use proper typing
segun Jul 20, 2023
ee13864
Fix imports in stories
segun Jul 20, 2023
59f1fa1
Fix couple of nits
segun Jul 20, 2023
b038d11
Sort imports
segun Jul 20, 2023
faaccc9
fixed see details i18n
segun Jul 20, 2023
dd56a76
Fix snapshot
segun Jul 20, 2023
58b75a5
Sorted imports
segun Jul 20, 2023
888e497
Lint fixes
segun Jul 24, 2023
a40f3e5
check flagType early
segun Jul 25, 2023
7b435df
lint fixes
segun Jul 25, 2023
ddd486c
Sort attack types in alphabetical order
segun Jul 26, 2023
41ab826
changes to make it look good when added to transaction page
segun Jul 27, 2023
6e01e02
margin between lines to make it look like figma
segun Jul 28, 2023
bf8dbde
Use security tick
segun Jul 28, 2023
ffc124e
Fix snapshot
segun Jul 28, 2023
6bd01d9
rename blockaid
segun Jul 28, 2023
efabe87
snapshot fix
segun Jul 28, 2023
5bcbdd5
Fix PR comments.
segun Jul 31, 2023
3bdcb5a
fall back to others as reason for non existent reason
segun Jul 31, 2023
648506d
For failed Response, show a normal banner alert
segun Aug 1, 2023
bb0de2b
Fix snapshot and test titles
segun Aug 2, 2023
76d605b
do not show blockaid if no env. variable
segun Aug 3, 2023
e33e442
lint fix
segun Aug 3, 2023
316e30c
move Accordion into renderDetails to avoid rendering if no details ar…
segun Aug 4, 2023
3ec9aa5
Add title for failed
segun Aug 4, 2023
19a63c2
Merge branch 'main' into blockaid-banners-implementation
segun Aug 15, 2023
d56bb2c
Merge branch 'main' into blockaid-banners-implementation
segun Aug 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions app/components/UI/BlockaidBanner/AttributionLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { Linking, StyleSheet } from 'react-native';

import { strings } from '../../../../locales/i18n';
import { DEFAULT_BANNERBASE_DESCRIPTION_TEXTVARIANT } from '../../../component-library/components/Banners/Banner/foundation/BannerBase/BannerBase.constants';
import Text from '../../../component-library/components/Texts/Text/Text';
jpuri marked this conversation as resolved.
Show resolved Hide resolved
import { useTheme } from '../../../util/theme';

const createStyles = (colors: any) =>
StyleSheet.create({
attributionLink: { color: colors.primary.default },
});

const AttributionLink = () => {
const { colors } = useTheme();
const styles = createStyles(colors);

return (
<Text
suppressHighlighting
style={styles.attributionLink}
variant={DEFAULT_BANNERBASE_DESCRIPTION_TEXTVARIANT}
onPress={() => {
Linking.openURL(strings('blockaid_banner.attribution_link'));
}}
>
{strings('blockaid_banner.attribution_link_name')}
</Text>
);
};

export default AttributionLink;
28 changes: 28 additions & 0 deletions app/components/UI/BlockaidBanner/BlockaidBanner.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const ATTRIBUTION_LINE_TEST_ID = 'blockaid-banner-attribution-line';

import { Reason } from './BlockaidBanner.types';

export const REASON_DESCRIPTION_I18N_KEY_MAP = Object.freeze({
[Reason.approvalFarming]: 'blockaid_banner.approval_farming_description',
[Reason.blurFarming]: 'blockaid_banner.blur_farming_description',
[Reason.maliciousDomain]: 'blockaid_banner.malicious_domain_description',
[Reason.failed]: 'blockaid_banner.failed_description',
[Reason.other]: 'blockaid_banner.other_description',
[Reason.permitFarming]: 'blockaid_banner.approval_farming_description',
[Reason.rawNativeTokenTransfer]:
'blockaid_banner.transfer_farming_description',
[Reason.rawSignatureFarming]:
'blockaid_banner.raw_signature_farming_description',
[Reason.seaportFarming]: 'blockaid_banner.seaport_farming_description',
[Reason.setApprovalForAllFarming]:
'blockaid_banner.approval_farming_description',
[Reason.tradeOrderFarming]: 'blockaid_banner.trade_order_farming_description',
[Reason.transferFarming]: 'blockaid_banner.transfer_farming_description',
[Reason.transferFromFarming]: 'blockaid_banner.transfer_farming_description',
[Reason.unfairTrade]: 'blockaid_banner.unfair_trade_description',
});

export const REASON_TITLE_I18N_KEY_MAP: Record<string, string> = Object.freeze({
[Reason.rawSignatureFarming]: 'blockaid_banner.suspicious_request_title',
[Reason.failed]: 'blockaid_banner.failed_title',
});
58 changes: 58 additions & 0 deletions app/components/UI/BlockaidBanner/BlockaidBanner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* eslint-disable no-console */
import React from 'react';

import { select, text } from '@storybook/addon-knobs';
import { storiesOf } from '@storybook/react-native';

import {
SAMPLE_BANNERALERT_DESCRIPTION,
SAMPLE_BANNERALERT_TITLE,
} from '../../../component-library/components/Banners/Banner/variants/BannerAlert/BannerAlert.constants';
import { storybookPropsGroupID } from '../../../component-library/constants/storybook.constants';
import BlockaidBanner from './BlockaidBanner';
import { BlockaidBannerProps, FlagType, Reason } from './BlockaidBanner.types';

export const getBlockaidBannerStoryProps = (): BlockaidBannerProps => {
const flagTypeSelector = select(
'flagType',
FlagType,
FlagType.Warning,
storybookPropsGroupID,
);

const reasonSelector = select(
'reason',
Reason,
Reason.approvalFarming,
storybookPropsGroupID,
);

const title = text('title', SAMPLE_BANNERALERT_TITLE, storybookPropsGroupID);
const description = text(
'description',
SAMPLE_BANNERALERT_DESCRIPTION,
storybookPropsGroupID,
);

return {
title,
description,
reason: reasonSelector,
flagType: flagTypeSelector,
features: [
'Operator is an EOA',
'Operator is untrusted according to previous activity',
],
};
};

const BlockaidBannerStory = () => (
<BlockaidBanner {...getBlockaidBannerStoryProps()} />
);

storiesOf('Components / UI / BlockaidBanner', module).add(
'BlockaidBanner',
BlockaidBannerStory,
);

export default BlockaidBannerStory;
34 changes: 34 additions & 0 deletions app/components/UI/BlockaidBanner/BlockaidBanner.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Third party dependencies.
import { Theme } from '../../../util/theme/models';
import { StyleSheet, ViewStyle } from 'react-native';
import { BlockaidBannerStyleSheetVars } from './BlockaidBanner.types';
/**
* Style sheet function for BannerAlert component.
*
* @param params Style sheet params.
* @param params.theme App theme from ThemeContext.
* @param params.vars Inputs that the style sheet depends on.
* @returns StyleSheet object.
*/
const styleSheet = (_params: {
theme: Theme;
vars: BlockaidBannerStyleSheetVars;
}) =>
StyleSheet.create({
attributionBase: Object.assign({
height: 24,
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'flex-start',
} as ViewStyle),
attributionItem: {
marginRight: 4,
},
detailsItem: {
marginBottom: 4,
},
details: { marginLeft: 10, marginBottom: 10 },
securityTickIcon: { marginTop: 4 },
});

export default styleSheet;
142 changes: 142 additions & 0 deletions app/components/UI/BlockaidBanner/BlockaidBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from 'react';

import { fireEvent, render } from '@testing-library/react-native';

import { TESTID_ACCORDION_CONTENT } from '../../../component-library/components/Accordions/Accordion/Accordion.constants';
import { TESTID_ACCORDIONHEADER } from '../../../component-library/components/Accordions/Accordion/foundation/AccordionHeader/AccordionHeader.constants';
import { BANNERALERT_TEST_ID } from '../../../component-library/components/Banners/Banner/variants/BannerAlert/BannerAlert.constants';
import BlockaidBanner from './BlockaidBanner';
import { ATTRIBUTION_LINE_TEST_ID } from './BlockaidBanner.constants';
import { FlagType, Reason } from './BlockaidBanner.types';

jest.mock('../../../util/blockaid', () => ({
showBlockaidUI: jest.fn().mockReturnValue(true),
}));

describe('BlockaidBanner', () => {
const mockFeatures = [
'We found attack vectors in this request',
'This request shows a fake token name and icon.',
'If you approve this request, a third party known for scams might take all your assets.',
'Operator is an EOA',
'Operator is untrusted according to previous activity',
];

it('should render correctly', () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Warning}
reason={Reason.approvalFarming}
features={mockFeatures}
/>,
);

expect(wrapper).toMatchSnapshot();
});

it('should render correctly with reason "raw_signature_farming"', async () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Malicious}
reason={Reason.rawSignatureFarming}
features={mockFeatures}
/>,
);

expect(wrapper).toMatchSnapshot();
expect(await wrapper.queryByTestId(TESTID_ACCORDIONHEADER)).toBeDefined();
expect(
await wrapper.getByText('This is a suspicious request'),
).toBeDefined();
expect(
await wrapper.getByText(
'If you approve this request, you might lose your assets.',
),
).toBeDefined();
});

it('should render correctly with attribution link', async () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Malicious}
reason={Reason.rawSignatureFarming}
features={mockFeatures}
/>,
);

expect(await wrapper.queryByTestId(ATTRIBUTION_LINE_TEST_ID)).toBeDefined();
});

it('should render correctly with list attack details', async () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Malicious}
reason={Reason.approvalFarming}
features={mockFeatures}
/>,
);

expect(wrapper).toMatchSnapshot();
expect(await wrapper.queryByTestId(TESTID_ACCORDIONHEADER)).toBeDefined();
expect(await wrapper.queryByTestId(TESTID_ACCORDION_CONTENT)).toBeNull();

fireEvent.press(await wrapper.getByText('See details'));

expect(await wrapper.queryByTestId(TESTID_ACCORDION_CONTENT)).toBeDefined();
expect(
await wrapper.queryByText('We found attack vectors in this request'),
).toBeDefined();
expect(
await wrapper.queryByText(
'This request shows a fake token name and icon.',
),
).toBeDefined();
expect(
await wrapper.queryByText(
'If you approve this request, a third party known for scams might take all your assets.',
),
).toBeDefined();
expect(await wrapper.queryByText('Operator is an EOA')).toBeDefined();
expect(
await wrapper.queryByText(
'Operator is untrusted according to previous activity',
),
).toBeDefined();
});

it('should not render if flagType is benign', async () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Benign}
reason={Reason.rawSignatureFarming}
features={mockFeatures}
/>,
);

expect(wrapper).toMatchSnapshot();
expect(await wrapper.queryByTestId(TESTID_ACCORDIONHEADER)).toBeNull();
expect(await wrapper.queryByTestId(TESTID_ACCORDION_CONTENT)).toBeNull();
});

it('should render normal banner alert if flagType is failed', async () => {
const wrapper = render(
<BlockaidBanner
flagType={FlagType.Failed}
reason={Reason.rawSignatureFarming}
features={mockFeatures}
/>,
);

expect(wrapper).toMatchSnapshot();

expect(await wrapper.queryByTestId(TESTID_ACCORDIONHEADER)).toBeNull();
expect(await wrapper.queryByTestId(TESTID_ACCORDION_CONTENT)).toBeNull();
expect(await wrapper.queryByTestId(BANNERALERT_TEST_ID)).toBeDefined();
expect(await wrapper.queryByText('Request may not be safe')).toBeDefined();
expect(
await wrapper.queryByText(
'Because of an error, this request was not verified by the security provider. Proceed with caution.',
),
).toBeDefined();
});
});
Loading
Loading