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

feat: Add Redesign Permit support; fix: InfoRow padding and alignment #13369

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ const AccountNetworkInfoExpanded = () => {
return (
<View>
<InfoSection>
<InfoRow label={strings('confirm.account')}>
<InfoRow label={strings('confirm.label.account')}>
{renderShortAddress(accountAddress, 5)}
</InfoRow>
<InfoRow label={strings('confirm.balance')}>
<InfoRow label={strings('confirm.label.balance')}>
{accountFiatBalance}
</InfoRow>
</InfoSection>
<InfoSection>
<InfoRow label={strings('confirm.network')}>
<InfoRow label={strings('confirm.label.network')}>
<Network chainId={chainId} />
</InfoRow>
</InfoSection>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const createStyles = (depth: number) =>
},
dataRow: {
paddingHorizontal: 0,
paddingBottom: 16,
paddingBottom: 8,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ const InfoRowOrigin = () => {
testID={ConfirmationPageSectionsSelectorIDs.ORIGIN_INFO_SECTION}
>
<InfoRow
label={strings('confirm.request_from')}
label={strings('confirm.label.request_from')}
tooltip={strings('confirm.personal_sign_tooltip')}
>
<DisplayURL url={approvalRequest.origin} />
</InfoRow>
{isSIWEMessage && (
<InfoRow
label={strings('confirm.signing_in_with')}
label={strings('confirm.label.signing_in_with')}
testID={
ConfirmationPageSectionsSelectorIDs.SIWE_SIGNING_ACCOUNT_INFO_SECTION
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StyleSheet } from 'react-native';

const styleSheet = () => StyleSheet.create({
dividerContainer: {
marginTop: 4,
marginBottom: 12,
},
});

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';

import renderWithProvider from '../../../../../../../../util/test/renderWithProvider';
import {
typedSignV3ConfirmationState,
typedSignV4ConfirmationState,
} from '../../../../../../../../util/test/confirm-data-helpers';
import { InfoSectionOriginAndDetails } from './InfoSectionOriginAndDetails';

describe('InfoSectionOriginAndDetails', () => {
it('renders origin', () => {
const { getByText } = renderWithProvider(<InfoSectionOriginAndDetails />, {
state: typedSignV4ConfirmationState,
});

expect(getByText('Request from')).toBeTruthy();
expect(getByText('metamask.github.io')).toBeTruthy();
});

it('renders "Interacting with" if associated with a valid verifying contract', () => {
const { getByText } = renderWithProvider(<InfoSectionOriginAndDetails />, {
state: typedSignV4ConfirmationState,
});

expect(getByText('Request from')).toBeTruthy();
});

it('renders Spender if it is a Permit', () => {
const { getByText } = renderWithProvider(<InfoSectionOriginAndDetails />, {
state: typedSignV4ConfirmationState,
});

expect(getByText('Interacting with')).toBeTruthy();
expect(getByText('0xCcCCc...ccccC')).toBeTruthy();
});

it('does not render Spender if it is not a Permit', () => {
const { queryByText } = renderWithProvider(<InfoSectionOriginAndDetails />, {
state: typedSignV3ConfirmationState,
});

expect(queryByText('Spender')).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';

import { ConfirmationPageSectionsSelectorIDs } from '../../../../../../../../../e2e/selectors/Confirmation/ConfirmationView.selectors';
import { strings } from '../../../../../../../../../locales/i18n';
import { useStyles } from '../../../../../../../../component-library/hooks';
import InfoRow from '../../../../UI/InfoRow';
import { InfoRowDivider } from '../../../../UI/InfoRow/Divider/Divider';
import InfoSection from '../../../../UI/InfoRow/InfoSection';
import InfoRowAddress from '../../../../UI/InfoRow/InfoValue/Address';
import DisplayURL from '../../../../UI/InfoRow/InfoValue/DisplayURL';
import { isRecognizedPermit, parseTypedDataMessageFromSignatureRequest } from '../../../../../utils/signature';
import { useSignatureRequest } from '../../../../../hooks/useSignatureRequest';
import useApprovalRequest from '../../../../../hooks/useApprovalRequest';
import { View } from 'react-native';
import styleSheet from './InfoSectionOriginAndDetails.styles';
import { isValidAddress } from 'ethereumjs-util';

export const InfoSectionOriginAndDetails = () => {
const { styles } = useStyles(styleSheet, {});

// signatureRequest from SignatureController does not include the origin
// so we need to use approvalRequest
const { approvalRequest } = useApprovalRequest();
const origin = approvalRequest?.origin as string;

const signatureRequest = useSignatureRequest();
const isPermit = isRecognizedPermit(signatureRequest);

const {
domain: { verifyingContract } = {},

Check failure on line 30 in app/components/Views/confirmations/components/Confirm/Info/TypedSignV3V4/InfoSectionOriginAndDetails/InfoSectionOriginAndDetails.tsx

View workflow job for this annotation

GitHub Actions / scripts (lint:tsc)

Initializer provides no value for this binding element and the binding element has no default value.
message: { spender } = {},

Check failure on line 31 in app/components/Views/confirmations/components/Confirm/Info/TypedSignV3V4/InfoSectionOriginAndDetails/InfoSectionOriginAndDetails.tsx

View workflow job for this annotation

GitHub Actions / scripts (lint:tsc)

Initializer provides no value for this binding element and the binding element has no default value.
} = parseTypedDataMessageFromSignatureRequest(signatureRequest) ?? {};

if (!signatureRequest) {
return null;
}

const chainId = signatureRequest.chainId;

return (
<InfoSection
testID={ConfirmationPageSectionsSelectorIDs.ORIGIN_INFO_SECTION}
>
{isPermit && spender && (
<>
<InfoRow label={strings('confirm.label.spender')}>
<InfoRowAddress address={spender} chainId={chainId} />
</InfoRow>
<View style={styles.dividerContainer}>
<InfoRowDivider />
</View>
</>
)}
<InfoRow
label={strings('confirm.request_from')}
tooltip={strings('confirm.personal_sign_tooltip')}
>
<DisplayURL url={origin} />
</InfoRow>
{isValidAddress(verifyingContract) && (
<InfoRow label={strings('confirm.label.interacting_with')}>
<InfoRowAddress
address={verifyingContract}
chainId={chainId}
/>
</InfoRow>
)}
</InfoSection>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { InfoSectionOriginAndDetails } from './InfoSectionOriginAndDetails';
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const styleSheet = (params: { theme: Theme }) => {
},
dataRow: {
paddingHorizontal: 0,
paddingBottom: 16,
paddingBottom: 8,
},
title: {
color: theme.colors.text.default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const Message = () => {
messageCollapsed={
isSimulationSupported ? undefined : (
<InfoRow
label={strings('confirm.primary_type')}
label={strings('confirm.label.primary_type')}
style={styles.collpasedInfoRow}
>
{primaryType}
Expand All @@ -80,7 +80,7 @@ const Message = () => {
<View>
<Text style={styles.title}>{strings('confirm.message')}</Text>
<InfoRow
label={strings('confirm.primary_type')}
label={strings('confirm.label.primary_type')}
style={styles.dataRow}
>
{primaryType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { waitFor } from '@testing-library/react-native';

import renderWithProvider from '../../../../../../../../../util/test/renderWithProvider';
import { typedSignV4ConfirmationState } from '../../../../../../../../../util/test/confirm-data-helpers';
import { typedSignV4ConfirmationState, typedSignV4NFTConfirmationState } from '../../../../../../../../../util/test/confirm-data-helpers';
import PermitSimulation from './TypedSignPermit';

jest.mock('../../../../../../../../../core/Engine', () => ({
Expand All @@ -14,20 +14,37 @@ jest.mock('../../../../../../../../../core/Engine', () => ({
}));

describe('PermitSimulation', () => {
it('should render correctly for personal sign', async () => {
it('should render correctly for Permit', async () => {
const { getByText } = renderWithProvider(<PermitSimulation />, {
state: typedSignV4ConfirmationState,
});

expect(getByText('Estimated changes')).toBeDefined();
expect(getByText('Estimated changes')).toBeTruthy();
expect(
getByText(
"You're giving the spender permission to spend this many tokens from your account.",
),
).toBeDefined();
expect(getByText('Spending cap')).toBeDefined();
expect(getByText('0xCcCCc...ccccC')).toBeDefined();
).toBeTruthy();
expect(getByText('Spending cap')).toBeTruthy();
expect(getByText('0xCcCCc...ccccC')).toBeTruthy();

await waitFor(() => expect(getByText('3,000')).toBeDefined());
await waitFor(() => expect(getByText('3,000')).toBeTruthy());
});

it('should render correctly for Permit NFTs', async () => {
const { getByText } = renderWithProvider(<PermitSimulation />, {
state: typedSignV4NFTConfirmationState,
});

expect(getByText('Estimated changes')).toBeTruthy();
expect(
getByText(
"You're giving the spender permission to spend this many tokens from your account.",
),
).toBeTruthy();
expect(getByText('Withdraw')).toBeTruthy();
expect(getByText('0xC3644...1FE88')).toBeTruthy();

await waitFor(() => expect(getByText('#3606393')).toBeTruthy());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ const PermitSimulation = () => {

const tokenDetails = extractTokenDetailsByPrimaryType(message, primaryType);

const isNFT = tokenId !== undefined;
const isNFT = tokenId !== undefined && tokenId !== '0';

const labelChangeType = isNFT
? strings('confirm.simulation.label_change_type_permit_nft')
: strings('confirm.simulation.label_change_type_permit');
Expand All @@ -87,24 +88,25 @@ const PermitSimulation = () => {

<InfoRow label={labelChangeType}>
{Array.isArray(tokenDetails) ? (
<View style={styles.permitValues}>
<>
{tokenDetails.map(
(
{ token, amount }: { token: string; amount: string },
i: number,
) => (
<PermitSimulationValueDisplay
key={`${token}-${i}`}
labelChangeType={labelChangeType}
networkClientId={networkClientId}
primaryType={primaryType}
tokenContract={safeToChecksumAddress(token)}
value={amount}
chainId={chainId}
/>
<View style={styles.permitValues} key={`${token}-${i}`}>
<PermitSimulationValueDisplay
labelChangeType={labelChangeType}
networkClientId={networkClientId}
primaryType={primaryType}
tokenContract={safeToChecksumAddress(token)}
value={amount}
chainId={chainId}
/>
</View>
),
)}
</View>
</>
) : (
<PermitSimulationValueDisplay
labelChangeType={labelChangeType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,14 @@ const SimulationValueDisplay: React.FC<SimulationValueDisplayParams> = ({
tokenDetails as TokenDetailsERC20,
);

const isNFT = tokenId !== undefined && tokenId !== '0';

const tokenAmount =
isNumberValue(value) && !tokenId
? calcTokenAmount(value as number | string, tokenDecimals)
: null;
const isValidTokenAmount =
!isNFT &&
tokenAmount !== null &&
tokenAmount !== undefined &&
tokenAmount instanceof BigNumber;
Expand Down Expand Up @@ -163,7 +166,7 @@ const SimulationValueDisplay: React.FC<SimulationValueDisplayParams> = ({
{
<AnimatedPulse isPulsing={isPendingTokenDetails} testID="simulation-value-display-loader">
<ButtonPill
isDisabled={!!tokenId || tokenId === '0'}
isDisabled={isNFT}
onPress={handlePressTokenValue}
onPressIn={handlePressTokenValue}
onPressOut={handlePressTokenValue}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import AccountNetworkInfo from '../../AccountNetworkInfo';
import InfoRowOrigin from '../Shared/InfoRowOrigin';
import { InfoSectionOriginAndDetails } from './InfoSectionOriginAndDetails';
import Message from './Message';
import TypedSignV3V4Simulation from './Simulation';

const TypedSignV3V4 = () => (
<>
<AccountNetworkInfo />
<TypedSignV3V4Simulation />
<InfoRowOrigin />
<InfoSectionOriginAndDetails />
<Message />
</>
);
Expand Down
Loading
Loading