Skip to content

Commit

Permalink
feat: permit signature simulation info (#24862)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuri authored Jun 12, 2024
1 parent 9a5aeae commit 57b9c82
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 4 deletions.
3 changes: 3 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PermitSimulation renders component correctly 1`] = `
<div>
<div
class="mm-box mm-box--margin-bottom-4 mm-box--padding-2 mm-box--background-color-background-default mm-box--rounded-md"
>
<div
class="mm-box confirm-info-row mm-box--margin-top-2 mm-box--margin-bottom-2 mm-box--padding-right-2 mm-box--padding-left-2 mm-box--display-flex mm-box--flex-direction-row mm-box--flex-wrap-wrap mm-box--justify-content-space-between mm-box--color-text-default mm-box--rounded-lg"
style="overflow-wrap: anywhere; min-height: 24px;"
>
<div
class="mm-box mm-box--display-flex mm-box--flex-direction-row mm-box--justify-content-center mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-md-medium mm-box--color-inherit"
>
Estimated changes
</p>
<div>
<div
aria-describedby="tippy-tooltip-1"
class=""
data-original-title="Estimated changes are what might happen if you go through with this transaction. This is just a prediction, not a guarantee."
data-tooltipped=""
style="display: flex;"
tabindex="0"
>
<span
class="mm-box mm-icon mm-icon--size-sm mm-box--margin-left-1 mm-box--display-inline-block mm-box--color-icon-muted"
style="mask-image: url('./images/icons/question.svg');"
/>
</div>
</div>
</div>
<div
class="mm-box mm-box--display-flex mm-box--gap-2 mm-box--flex-wrap-wrap mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-md mm-box--color-inherit"
style="white-space: pre-wrap;"
>
This transaction gives permission to withdraw your tokens
</p>
</div>
</div>
<div
class="mm-box confirm-info-row mm-box--margin-top-2 mm-box--margin-bottom-2 mm-box--padding-right-2 mm-box--padding-left-2 mm-box--display-flex mm-box--flex-direction-row mm-box--flex-wrap-wrap mm-box--justify-content-space-between mm-box--color-text-default mm-box--rounded-lg"
style="overflow-wrap: anywhere; min-height: 24px;"
>
<div
class="mm-box mm-box--display-flex mm-box--flex-direction-row mm-box--justify-content-center mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-md-medium mm-box--color-inherit"
>
Approve spend limit
</p>
</div>
<div
class="mm-box"
>
<div
class="mm-box mm-box--display-flex"
>
<div
class="mm-box mm-box--margin-inline-end-1 mm-box--display-inline"
>
<p
class="mm-box mm-text mm-text--body-md mm-text--text-align-center mm-box--padding-inline-2 mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-xl"
>
3000
</p>
</div>
<div>
<div
class="name name__missing"
>
<span
class="mm-box name__icon mm-icon mm-icon--size-md mm-box--display-inline-block mm-box--color-inherit"
style="mask-image: url('./images/icons/question.svg');"
/>
<p
class="mm-box mm-text name__value mm-text--body-md mm-box--color-text-default"
>
0xCcCCc...ccccC
</p>
</div>
</div>
</div>
<div
class="mm-box"
/>
</div>
</div>
</div>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as PermitSimulation } from './permit-simulation';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';

import mockState from '../../../../../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../../../../../test/lib/render-helpers';
import { permitSignatureMsg } from '../../../../../../../../test/data/confirmations/typed_sign';
import PermitSimulation from './permit-simulation';

describe('PermitSimulation', () => {
it('renders component correctly', () => {
const state = {
...mockState,
confirm: {
currentConfirmation: permitSignatureMsg,
},
};
const mockStore = configureMockStore([])(state);
const { container } = renderWithProvider(<PermitSimulation />, mockStore);
expect(container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { NameType } from '@metamask/name-controller';

import { Numeric } from '../../../../../../../../shared/modules/Numeric';
import Name from '../../../../../../../components/app/name/name';
import {
ConfirmInfoRow,
ConfirmInfoRowText,
} from '../../../../../../../components/app/confirm/info/row';
import { useI18nContext } from '../../../../../../../hooks/useI18nContext';
import { currentConfirmationSelector } from '../../../../../../../selectors';
import { Box, Text } from '../../../../../../../components/component-library';
import {
BackgroundColor,
BorderRadius,
Display,
TextAlign,
} from '../../../../../../../helpers/constants/design-system';
import { parseTypedDataMessage } from '../../../../../utils';
import { SignatureRequestType } from '../../../../../types/confirm';
import useTokenExchangeRate from '../../../../../../../components/app/currency-input/hooks/useTokenExchangeRate';
import { IndividualFiatDisplay } from '../../../../simulation-details/fiat-display';

const PermitSimulation: React.FC = () => {
const t = useI18nContext();
const currentConfirmation = useSelector(
currentConfirmationSelector,
) as SignatureRequestType;

const {
domain: { verifyingContract },
message: { value },
} = parseTypedDataMessage(currentConfirmation.msgParams?.data as string);

const exchangeRate = useTokenExchangeRate(verifyingContract);

const fiatValue = useMemo(() => {
if (exchangeRate && value) {
return exchangeRate.times(new Numeric(value, 10)).toNumber();
}
return undefined;
}, [exchangeRate, value]);

return (
<Box
backgroundColor={BackgroundColor.backgroundDefault}
borderRadius={BorderRadius.MD}
padding={2}
marginBottom={4}
>
<ConfirmInfoRow
label={t('simulationDetailsTitle')}
tooltip={t('simulationDetailsTitleTooltip')}
>
<ConfirmInfoRowText text={t('permitSimulationDetailInfo')} />
</ConfirmInfoRow>
<ConfirmInfoRow label={t('approve')}>
<Box>
<Box display={Display.Flex}>
<Box display={Display.Inline} marginInlineEnd={1}>
<Text
backgroundColor={BackgroundColor.backgroundAlternative}
borderRadius={BorderRadius.XL}
paddingInline={2}
textAlign={TextAlign.Center}
>
{value}
</Text>
</Box>
<Name value={verifyingContract} type={NameType.ETHEREUM_ADDRESS} />
</Box>
<Box>
{fiatValue && <IndividualFiatDisplay fiatAmount={fiatValue} />}
</Box>
</Box>
</ConfirmInfoRow>
</Box>
);
};

export default PermitSimulation;
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ describe('TypedSignInfo', () => {
expect(container).toMatchSnapshot();
});

it('display simulation details for permit signature', () => {
const state = {
...mockState,
confirm: {
currentConfirmation: permitSignatureMsg,
},
};
const mockStore = configureMockStore([])(state);
const { getByText } = renderWithProvider(<TypedSignInfo />, mockStore);
expect(getByText('Estimated changes')).toBeDefined();
});

it('displays "Approving to" for permit signature type', () => {
const state = {
...mockState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { EIP712_PRIMARY_TYPE_PERMIT } from '../../../../constants';
import { SignatureRequestType } from '../../../../types/confirm';
import { parseTypedDataMessage } from '../../../../utils';
import { ConfirmInfoRowTypedSignData } from '../../row/typed-sign-data/typedSignData';
import { PermitSimulation } from './permit-simulation';

const TypedSignInfo: React.FC = () => {
const t = useI18nContext();
Expand All @@ -31,13 +32,14 @@ const TypedSignInfo: React.FC = () => {
}

const {
domain,
domain: { verifyingContract },
primaryType,
message: { spender },
} = parseTypedDataMessage(currentConfirmation.msgParams.data as string);

return (
<>
{primaryType === EIP712_PRIMARY_TYPE_PERMIT && <PermitSimulation />}
<Box
backgroundColor={BackgroundColor.backgroundDefault}
borderRadius={BorderRadius.MD}
Expand All @@ -48,7 +50,7 @@ const TypedSignInfo: React.FC = () => {
<>
<Box padding={2}>
<ConfirmInfoRow label={t('approvingTo')}>
<ConfirmInfoRowAddress address={verifyingContract} />
<ConfirmInfoRowAddress address={spender} />
</ConfirmInfoRow>
</Box>
<ConfirmInfoRowDivider />
Expand All @@ -62,10 +64,10 @@ const TypedSignInfo: React.FC = () => {
<ConfirmInfoRowUrl url={currentConfirmation.msgParams.origin} />
</ConfirmInfoRow>
</Box>
{isValidAddress(domain.verifyingContract) && (
{isValidAddress(verifyingContract) && (
<Box padding={2}>
<ConfirmInfoRow label={t('interactingWith')}>
<ConfirmInfoRowAddress address={domain.verifyingContract} />
<ConfirmInfoRowAddress address={verifyingContract} />
</ConfirmInfoRow>
</Box>
)}
Expand Down

0 comments on commit 57b9c82

Please sign in to comment.