mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Signature-Request refactor (#19104)
* refactoring signature-request and adding test coverage * adding storybook and removing the reduntant files: * adding new components from * replacing <SiteOrigin/> with <TagUrl/> * updating review comments from Jyoti and George * adding the hook * refactoring in the changes from #18770 MMI PR * adding new hook for the MMISign changes * updating lavamoat * updating lavamoat * removing a commeted line * updating the sign check with accountType conditional * fixing build issues * updating the review comments on the hooks * updating signatureRequestHeader * lint fix * fixing test failure * lint fix * updating review comments * adding the renamed hook * updating the origin url * fixing test failure * migrating changes from #19184 * updating snapshot * fixing e2e failure * fixing e2e failure * addressing review comments from Joao * migrating chnages from #19892 * moving shallowEqual inside of mmi build * migrating changes from #19881 * fixing build failure * migrating changes from #19949 * migrating changes from #19468 * updating snapshot * updating snapshot * updating QA review comments * fixing full screen height issue from develop * migrating changes from #20083 * fixing snapshot
This commit is contained in:
parent
6ce80fe997
commit
0c203d0518
@ -136,7 +136,7 @@ describe('Eth sign', function () {
|
||||
|
||||
await driver.waitForSelector({
|
||||
text: 'Reject 2 requests',
|
||||
tag: 'a',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
await verifyAndAssertEthSign(driver, DAPP_URL, expectedEthSignMessage);
|
||||
|
@ -102,7 +102,7 @@ describe('Personal sign', function () {
|
||||
|
||||
await driver.waitForSelector({
|
||||
text: 'Reject 2 requests',
|
||||
tag: 'a',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
const personalMessageRow = await driver.findElement(
|
||||
|
@ -160,7 +160,7 @@ describe('Sign Typed Data Signature Request', function () {
|
||||
|
||||
await driver.waitForSelector({
|
||||
text: 'Reject 2 requests',
|
||||
tag: 'a',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
await verifyAndAssertSignTypedData(
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
} from '../../../helpers/utils/util';
|
||||
import { stripHexPrefix } from '../../../../shared/modules/hexstring-utils';
|
||||
import { isSuspiciousResponse } from '../../../../shared/modules/security-provider.utils';
|
||||
import Button from '../../ui/button';
|
||||
import SiteOrigin from '../../ui/site-origin';
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import { PageContainerFooter } from '../../ui/page-container';
|
||||
@ -23,6 +22,7 @@ import {
|
||||
FontWeight,
|
||||
TextAlign,
|
||||
TextColor,
|
||||
Size,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
IconColor,
|
||||
DISPLAY,
|
||||
@ -31,12 +31,20 @@ import {
|
||||
BackgroundColor,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
|
||||
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
||||
import {
|
||||
ButtonLink,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
Icon,
|
||||
IconName,
|
||||
Text,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../component-library';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import { Icon, IconName, Text } from '../../component-library';
|
||||
import Box from '../../ui/box/box';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
|
||||
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
||||
|
||||
import SignatureRequestHeader from '../signature-request-header';
|
||||
import SignatureRequestOriginalWarning from './signature-request-original-warning';
|
||||
|
||||
@ -366,13 +374,13 @@ export default class SignatureRequestOriginal extends Component {
|
||||
)}
|
||||
{this.renderFooter()}
|
||||
{messagesCount > 1 ? (
|
||||
<Button
|
||||
type="link"
|
||||
<ButtonLink
|
||||
size={Size.inherit}
|
||||
className="request-signature__container__reject"
|
||||
onClick={() => this.handleCancelAll()}
|
||||
>
|
||||
{rejectNText}
|
||||
</Button>
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
@ -50,7 +50,6 @@ export default function SignatureRequestSIWE({ txData }) {
|
||||
const t = useContext(I18nContext);
|
||||
const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
|
||||
const subjectMetadata = useSelector(getSubjectMetadata);
|
||||
|
||||
const messagesCount = useSelector(getTotalUnapprovedMessagesCount);
|
||||
const messagesList = useSelector(unconfirmedMessagesHashSelector);
|
||||
const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage);
|
||||
|
@ -41,7 +41,7 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
|
||||
of
|
||||
|
||||
1
|
||||
0
|
||||
</div>
|
||||
<div
|
||||
class="confirm-page-container-navigation__longtext"
|
||||
@ -51,7 +51,7 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
</div>
|
||||
<div
|
||||
class="confirm-page-container-navigation__container"
|
||||
style="visibility: initial;"
|
||||
style="visibility: hidden;"
|
||||
>
|
||||
<button
|
||||
class="confirm-page-container-navigation__arrow"
|
||||
@ -92,7 +92,7 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
style="height: 32px; width: 32px; border-radius: 16px;"
|
||||
>
|
||||
<div
|
||||
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 32px; height: 32px; display: inline-block; background: rgb(245, 228, 0);"
|
||||
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 32px; height: 32px; display: inline-block; background: rgb(24, 151, 242);"
|
||||
>
|
||||
<svg
|
||||
height="32"
|
||||
@ -101,25 +101,25 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
y="0"
|
||||
>
|
||||
<rect
|
||||
fill="#F2BE02"
|
||||
fill="#FA7900"
|
||||
height="32"
|
||||
transform="translate(5.482725134273373 -5.1953603353074) rotate(387.1 16 16)"
|
||||
transform="translate(-0.4291965340260923 -4.589212785086649) rotate(266.5 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="#01778E"
|
||||
fill="#F2A202"
|
||||
height="32"
|
||||
transform="translate(-2.6054559418209475 16.704606255523125) rotate(227.4 16 16)"
|
||||
transform="translate(2.4067447047270143 13.767950912205709) rotate(242.5 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="#C81435"
|
||||
fill="#C8144A"
|
||||
height="32"
|
||||
transform="translate(17.969690840904306 -12.592649051897114) rotate(422.3 16 16)"
|
||||
transform="translate(-19.808734531871874 -18.328897166120687) rotate(396.2 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
@ -134,7 +134,7 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
<span
|
||||
class="icon-with-fallback__fallback"
|
||||
>
|
||||
U
|
||||
L
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -144,12 +144,12 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative"
|
||||
>
|
||||
Unknown private network
|
||||
Localhost 8545
|
||||
</h6>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"
|
||||
>
|
||||
Antonio
|
||||
John Doe
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
@ -165,9 +165,9 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
align="end"
|
||||
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"
|
||||
>
|
||||
966.987986
|
||||
0
|
||||
|
||||
ABC
|
||||
ETH
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
@ -179,56 +179,48 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
class="signature-request__origin"
|
||||
>
|
||||
<div
|
||||
class="site-origin"
|
||||
class="box mm-tag-url box--padding-right-4 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--align-items-center box--background-color-background-default box--rounded-pill box--border-color-border-default box--border-width-1 box--border-style-solid"
|
||||
>
|
||||
<div
|
||||
class="chip chip--with-left-icon chip--border-color-border-muted chip--background-color-undefined"
|
||||
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-avatar-favicon mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-transparent box--border-style-solid box--border-width-1"
|
||||
>
|
||||
<div
|
||||
class="chip__left-icon"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="icon-with-fallback__fallback"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-default"
|
||||
style="mask-image: url('./images/icons/global.svg');"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="mm-box mm-text mm-text--body-md mm-text--ellipsis mm-box--color-text-alternative"
|
||||
>
|
||||
test
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-md mm-box--margin-top-4 mm-box--color-text-default"
|
||||
<h2
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-lg mm-box--margin-top-4 mm-box--color-text-default"
|
||||
>
|
||||
Signature request
|
||||
</h3>
|
||||
</h2>
|
||||
<h6
|
||||
align="center"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-box--margin-12 mm-box--margin-top-3 mm-box--color-text-alternative"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-text--text-align-center mm-box--margin-top-4 mm-box--margin-right-12 mm-box--margin-left-12 mm-box--color-text-alternative"
|
||||
>
|
||||
Only sign this message if you fully understand the content and trust the requesting site.
|
||||
</h6>
|
||||
<div>
|
||||
<a
|
||||
class="button btn-link signature-request-content__verify-contract-details"
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base signature-request-content__verify-contract-details mm-button-link mm-button-link--size-auto mm-text--body-md-medium mm-box--padding-right-0 mm-box--padding-left-0 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent"
|
||||
data-testid="verify-contract-details"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
<span
|
||||
class="mm-box mm-text mm-text--inherit mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</a>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -773,6 +765,12 @@ exports[`Signature Request Component render should match snapshot when we are us
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base signature-request__reject-all-button mm-button-link mm-button-link--size-inherit mm-text--body-md-medium mm-box--padding-right-0 mm-box--padding-left-0 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent"
|
||||
data-testid="signature-request-reject-all"
|
||||
>
|
||||
Reject 2 requests
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -818,7 +816,7 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
|
||||
of
|
||||
|
||||
1
|
||||
0
|
||||
</div>
|
||||
<div
|
||||
class="confirm-page-container-navigation__longtext"
|
||||
@ -828,7 +826,7 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
</div>
|
||||
<div
|
||||
class="confirm-page-container-navigation__container"
|
||||
style="visibility: initial;"
|
||||
style="visibility: hidden;"
|
||||
>
|
||||
<button
|
||||
class="confirm-page-container-navigation__arrow"
|
||||
@ -869,7 +867,7 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
style="height: 32px; width: 32px; border-radius: 16px;"
|
||||
>
|
||||
<div
|
||||
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 32px; height: 32px; display: inline-block; background: rgb(245, 228, 0);"
|
||||
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 32px; height: 32px; display: inline-block; background: rgb(24, 151, 242);"
|
||||
>
|
||||
<svg
|
||||
height="32"
|
||||
@ -878,25 +876,25 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
y="0"
|
||||
>
|
||||
<rect
|
||||
fill="#F2BE02"
|
||||
fill="#FA7900"
|
||||
height="32"
|
||||
transform="translate(5.482725134273373 -5.1953603353074) rotate(387.1 16 16)"
|
||||
transform="translate(-0.4291965340260923 -4.589212785086649) rotate(266.5 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="#01778E"
|
||||
fill="#F2A202"
|
||||
height="32"
|
||||
transform="translate(-2.6054559418209475 16.704606255523125) rotate(227.4 16 16)"
|
||||
transform="translate(2.4067447047270143 13.767950912205709) rotate(242.5 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="#C81435"
|
||||
fill="#C8144A"
|
||||
height="32"
|
||||
transform="translate(17.969690840904306 -12.592649051897114) rotate(422.3 16 16)"
|
||||
transform="translate(-19.808734531871874 -18.328897166120687) rotate(396.2 16 16)"
|
||||
width="32"
|
||||
x="0"
|
||||
y="0"
|
||||
@ -911,7 +909,7 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
<span
|
||||
class="icon-with-fallback__fallback"
|
||||
>
|
||||
U
|
||||
L
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -921,12 +919,12 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative"
|
||||
>
|
||||
Unknown private network
|
||||
Localhost 8545
|
||||
</h6>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"
|
||||
>
|
||||
Antonio
|
||||
John Doe
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
@ -942,9 +940,9 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
align="end"
|
||||
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"
|
||||
>
|
||||
1515270.174798
|
||||
0
|
||||
|
||||
DEF
|
||||
ETH
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
@ -956,56 +954,48 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
class="signature-request__origin"
|
||||
>
|
||||
<div
|
||||
class="site-origin"
|
||||
class="box mm-tag-url box--padding-right-4 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--align-items-center box--background-color-background-default box--rounded-pill box--border-color-border-default box--border-width-1 box--border-style-solid"
|
||||
>
|
||||
<div
|
||||
class="chip chip--with-left-icon chip--border-color-border-muted chip--background-color-undefined"
|
||||
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-avatar-favicon mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-transparent box--border-style-solid box--border-width-1"
|
||||
>
|
||||
<div
|
||||
class="chip__left-icon"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="icon-with-fallback__fallback"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-default"
|
||||
style="mask-image: url('./images/icons/global.svg');"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="mm-box mm-text mm-text--body-md mm-text--ellipsis mm-box--color-text-alternative"
|
||||
>
|
||||
test
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-md mm-box--margin-top-4 mm-box--color-text-default"
|
||||
<h2
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-lg mm-box--margin-top-4 mm-box--color-text-default"
|
||||
>
|
||||
Signature request
|
||||
</h3>
|
||||
</h2>
|
||||
<h6
|
||||
align="center"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-box--margin-12 mm-box--margin-top-3 mm-box--color-text-alternative"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-text--text-align-center mm-box--margin-top-4 mm-box--margin-right-12 mm-box--margin-left-12 mm-box--color-text-alternative"
|
||||
>
|
||||
Only sign this message if you fully understand the content and trust the requesting site.
|
||||
</h6>
|
||||
<div>
|
||||
<a
|
||||
class="button btn-link signature-request-content__verify-contract-details"
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base signature-request-content__verify-contract-details mm-button-link mm-button-link--size-auto mm-text--body-md-medium mm-box--padding-right-0 mm-box--padding-left-0 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent"
|
||||
data-testid="verify-contract-details"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
<span
|
||||
class="mm-box mm-text mm-text--inherit mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</a>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -1550,6 +1540,12 @@ exports[`Signature Request Component render should match snapshot when we want t
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base signature-request__reject-all-button mm-button-link mm-button-link--size-inherit mm-text--body-md-medium mm-box--padding-right-0 mm-box--padding-left-0 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent"
|
||||
data-testid="signature-request-reject-all"
|
||||
>
|
||||
Reject 2 requests
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1 +1 @@
|
||||
export { default } from './signature-request.container';
|
||||
export { default } from './signature-request';
|
||||
|
@ -14,7 +14,7 @@
|
||||
border-radius: 8px;
|
||||
|
||||
@include screen-sm-min {
|
||||
max-height: 80vh;
|
||||
max-height: max-content;
|
||||
min-height: 570px;
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
&__reject-all-button {
|
||||
margin-top: -15px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
&__origin {
|
||||
@ -58,12 +58,6 @@
|
||||
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
@include H6;
|
||||
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
a.signature-request-content__verify-contract-details {
|
||||
|
@ -1,427 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { memoize } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ethErrors, serializeError } from 'eth-rpc-errors';
|
||||
import LedgerInstructionField from '../ledger-instruction-field';
|
||||
import {
|
||||
sanitizeMessage,
|
||||
getURLHostName,
|
||||
getNetworkNameFromProviderType,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
shortenAddress,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../helpers/utils/util';
|
||||
import {
|
||||
MetaMetricsEventCategory,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
MetaMetricsEventName,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../../shared/constants/metametrics';
|
||||
import SiteOrigin from '../../ui/site-origin';
|
||||
import Button from '../../ui/button';
|
||||
import ContractDetailsModal from '../modals/contract-details-modal/contract-details-modal';
|
||||
import {
|
||||
TextAlign,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
IconColor,
|
||||
Display,
|
||||
BlockSize,
|
||||
BackgroundColor,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import NetworkAccountBalanceHeader from '../network-account-balance-header';
|
||||
import { Numeric } from '../../../../shared/modules/Numeric';
|
||||
import { isSuspiciousResponse } from '../../../../shared/modules/security-provider.utils';
|
||||
import { EtherDenomination } from '../../../../shared/constants/common';
|
||||
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
|
||||
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
||||
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
|
||||
import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils';
|
||||
|
||||
import {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
Box,
|
||||
Icon,
|
||||
IconName,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Text,
|
||||
} from '../../component-library';
|
||||
import Footer from './signature-request-footer';
|
||||
import Message from './signature-request-message';
|
||||
|
||||
export default class SignatureRequest extends PureComponent {
|
||||
static propTypes = {
|
||||
/**
|
||||
* The display content of transaction data
|
||||
*/
|
||||
txData: PropTypes.object.isRequired,
|
||||
/**
|
||||
* The display content of sender account
|
||||
*/
|
||||
fromAccount: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
balance: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
}).isRequired,
|
||||
/**
|
||||
* Check if the wallet is ledget wallet or not
|
||||
*/
|
||||
isLedgerWallet: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether the hardware wallet requires a connection disables the sign button if true.
|
||||
*/
|
||||
hardwareWalletRequiresConnection: PropTypes.bool.isRequired,
|
||||
/**
|
||||
* Current network chainId
|
||||
*/
|
||||
chainId: PropTypes.string,
|
||||
/**
|
||||
* RPC prefs of the current network
|
||||
*/
|
||||
rpcPrefs: PropTypes.object,
|
||||
nativeCurrency: PropTypes.string,
|
||||
currentCurrency: PropTypes.string.isRequired,
|
||||
conversionRate: PropTypes.number,
|
||||
providerConfig: PropTypes.object,
|
||||
subjectMetadata: PropTypes.object,
|
||||
unapprovedMessagesCount: PropTypes.number,
|
||||
clearConfirmTransaction: PropTypes.func.isRequired,
|
||||
history: PropTypes.object,
|
||||
mostRecentOverviewPage: PropTypes.string,
|
||||
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
|
||||
cancelAllApprovals: PropTypes.func.isRequired,
|
||||
resolvePendingApproval: PropTypes.func.isRequired,
|
||||
rejectPendingApproval: PropTypes.func.isRequired,
|
||||
completedTx: PropTypes.func.isRequired,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
showCustodianDeepLink: PropTypes.func,
|
||||
isNotification: PropTypes.bool,
|
||||
mmiOnSignCallback: PropTypes.func,
|
||||
// Used to show a warning if the signing account is not the selected account
|
||||
// Largely relevant for contract wallet custodians
|
||||
selectedAccount: PropTypes.object,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
trackEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
hasScrolledMessage: false,
|
||||
showContractDetails: false,
|
||||
};
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
componentDidMount() {
|
||||
if (this.props.txData.custodyId) {
|
||||
this.props.showCustodianDeepLink({
|
||||
custodyId: this.props.txData.custodyId,
|
||||
fromAddress: this.props.fromAccount.address,
|
||||
closeNotification: this.props.isNotification,
|
||||
onDeepLinkFetched: () => undefined,
|
||||
onDeepLinkShown: () => {
|
||||
this.context.trackEvent({
|
||||
category: MetaMetricsEventCategory.MMI,
|
||||
event: MetaMetricsEventName.SignatureDeeplinkDisplayed,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
setMessageRootRef(ref) {
|
||||
this.messageRootRef = ref;
|
||||
}
|
||||
|
||||
formatWallet(wallet) {
|
||||
return `${wallet.slice(0, 8)}...${wallet.slice(
|
||||
wallet.length - 8,
|
||||
wallet.length,
|
||||
)}`;
|
||||
}
|
||||
|
||||
memoizedParseMessage = memoize((data) => {
|
||||
const { message, domain = {}, primaryType, types } = JSON.parse(data);
|
||||
const sanitizedMessage = sanitizeMessage(message, primaryType, types);
|
||||
return { sanitizedMessage, domain, primaryType };
|
||||
});
|
||||
|
||||
handleCancelAll = () => {
|
||||
const {
|
||||
clearConfirmTransaction,
|
||||
history,
|
||||
mostRecentOverviewPage,
|
||||
showRejectTransactionsConfirmationModal,
|
||||
unapprovedMessagesCount,
|
||||
cancelAllApprovals,
|
||||
} = this.props;
|
||||
|
||||
showRejectTransactionsConfirmationModal({
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
onSubmit: async () => {
|
||||
await cancelAllApprovals();
|
||||
clearConfirmTransaction();
|
||||
history.push(mostRecentOverviewPage);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
providerConfig,
|
||||
txData: {
|
||||
msgParams: { data, origin, version },
|
||||
type,
|
||||
id,
|
||||
},
|
||||
fromAccount: { address, balance, name },
|
||||
isLedgerWallet,
|
||||
hardwareWalletRequiresConnection,
|
||||
chainId,
|
||||
rpcPrefs,
|
||||
txData,
|
||||
subjectMetadata,
|
||||
nativeCurrency,
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
unapprovedMessagesCount,
|
||||
resolvePendingApproval,
|
||||
rejectPendingApproval,
|
||||
completedTx,
|
||||
} = this.props;
|
||||
|
||||
const { t, trackEvent } = this.context;
|
||||
const {
|
||||
sanitizedMessage,
|
||||
domain: { verifyingContract },
|
||||
primaryType,
|
||||
} = this.memoizedParseMessage(data);
|
||||
const rejectNText = t('rejectRequestsN', [unapprovedMessagesCount]);
|
||||
const networkName = getNetworkNameFromProviderType(providerConfig.type);
|
||||
const currentNetwork =
|
||||
networkName === ''
|
||||
? providerConfig.nickname || t('unknownNetwork')
|
||||
: t(networkName);
|
||||
|
||||
const balanceInBaseAsset = conversionRate
|
||||
? formatCurrency(
|
||||
getValueFromWeiHex({
|
||||
value: balance,
|
||||
fromCurrency: nativeCurrency,
|
||||
toCurrency: currentCurrency,
|
||||
conversionRate,
|
||||
numberOfDecimals: 6,
|
||||
toDenomination: EtherDenomination.ETH,
|
||||
}),
|
||||
currentCurrency,
|
||||
)
|
||||
: new Numeric(balance, 16, EtherDenomination.WEI)
|
||||
.toDenomination(EtherDenomination.ETH)
|
||||
.round(6)
|
||||
.toBase(10)
|
||||
.toString();
|
||||
|
||||
const onSign = async () => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
if (this.props.mmiOnSignCallback) {
|
||||
await this.props.mmiOnSignCallback(txData);
|
||||
return;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
await resolvePendingApproval(id);
|
||||
completedTx(id);
|
||||
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Transactions,
|
||||
event: 'Confirm',
|
||||
properties: {
|
||||
action: 'Sign Request',
|
||||
legacy_event: true,
|
||||
type,
|
||||
version,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onCancel = async () => {
|
||||
await rejectPendingApproval(
|
||||
id,
|
||||
serializeError(ethErrors.provider.userRejectedRequest()),
|
||||
);
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Transactions,
|
||||
event: 'Cancel',
|
||||
properties: {
|
||||
action: 'Sign Request',
|
||||
legacy_event: true,
|
||||
type,
|
||||
version,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const messageIsScrollable =
|
||||
this.messageRootRef?.scrollHeight > this.messageRootRef?.clientHeight;
|
||||
|
||||
const targetSubjectMetadata = txData.msgParams.origin
|
||||
? subjectMetadata?.[txData.msgParams.origin]
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="signature-request">
|
||||
<ConfirmPageContainerNavigation />
|
||||
<div
|
||||
className="request-signature__account"
|
||||
data-testid="request-signature-account"
|
||||
>
|
||||
<NetworkAccountBalanceHeader
|
||||
networkName={currentNetwork}
|
||||
accountName={name}
|
||||
accountBalance={balanceInBaseAsset}
|
||||
tokenName={
|
||||
conversionRate ? currentCurrency?.toUpperCase() : nativeCurrency
|
||||
}
|
||||
accountAddress={address}
|
||||
/>
|
||||
</div>
|
||||
<div className="signature-request-content">
|
||||
{isSuspiciousResponse(txData?.securityProviderResponse) && (
|
||||
<SecurityProviderBannerMessage
|
||||
securityProviderResponse={txData.securityProviderResponse}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
this.props.selectedAccount.address === address ? null : (
|
||||
<Box
|
||||
className="request-signature__mismatch-info"
|
||||
display={Display.Flex}
|
||||
width={BlockSize.Full}
|
||||
padding={4}
|
||||
marginBottom={4}
|
||||
backgroundColor={BackgroundColor.primaryMuted}
|
||||
>
|
||||
<Icon
|
||||
name={IconName.Info}
|
||||
color={IconColor.infoDefault}
|
||||
marginRight={2}
|
||||
/>
|
||||
<Text
|
||||
variant={TextVariant.bodyXs}
|
||||
color={TextColor.textDefault}
|
||||
>
|
||||
{this.context.t('mismatchAccount', [
|
||||
shortenAddress(this.props.selectedAccount.address),
|
||||
shortenAddress(address),
|
||||
])}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
||||
<div className="signature-request__origin">
|
||||
<SiteOrigin
|
||||
siteOrigin={origin}
|
||||
iconSrc={targetSubjectMetadata?.iconUrl}
|
||||
iconName={getURLHostName(origin) || origin}
|
||||
chip
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text
|
||||
className="signature-request__content__title"
|
||||
variant={TextVariant.headingMd}
|
||||
as="h3"
|
||||
marginTop={4}
|
||||
>
|
||||
{this.context.t('sigRequest')}
|
||||
</Text>
|
||||
<Text
|
||||
className="request-signature__content__subtitle"
|
||||
variant={TextVariant.bodySm}
|
||||
as="h6"
|
||||
color={TextColor.textAlternative}
|
||||
align={TextAlign.Center}
|
||||
margin={12}
|
||||
marginTop={3}
|
||||
>
|
||||
{this.context.t('signatureRequestGuidance')}
|
||||
</Text>
|
||||
{verifyingContract ? (
|
||||
<div>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => this.setState({ showContractDetails: true })}
|
||||
className="signature-request-content__verify-contract-details"
|
||||
data-testid="verify-contract-details"
|
||||
>
|
||||
<Text
|
||||
variant={TextVariant.bodySm}
|
||||
as="h6"
|
||||
color={TextColor.primaryDefault}
|
||||
>
|
||||
{this.context.t('verifyContractDetails')}
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{isLedgerWallet ? (
|
||||
<div className="confirm-approve-content__ledger-instruction-wrapper">
|
||||
<LedgerInstructionField showDataInstruction />
|
||||
</div>
|
||||
) : null}
|
||||
<Message
|
||||
data={sanitizedMessage}
|
||||
onMessageScrolled={() => this.setState({ hasScrolledMessage: true })}
|
||||
setMessageRootRef={this.setMessageRootRef.bind(this)}
|
||||
messageRootRef={this.messageRootRef}
|
||||
messageIsScrollable={messageIsScrollable}
|
||||
primaryType={primaryType}
|
||||
/>
|
||||
<Footer
|
||||
cancelAction={onCancel}
|
||||
signAction={onSign}
|
||||
disabled={
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
Boolean(this.props.txData?.custodyId) ||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
hardwareWalletRequiresConnection ||
|
||||
(messageIsScrollable && !this.state.hasScrolledMessage)
|
||||
}
|
||||
/>
|
||||
{this.state.showContractDetails && (
|
||||
<ContractDetailsModal
|
||||
toAddress={verifyingContract}
|
||||
chainId={chainId}
|
||||
rpcPrefs={rpcPrefs}
|
||||
onClose={() => this.setState({ showContractDetails: false })}
|
||||
isContractRequestingSignature
|
||||
/>
|
||||
)}
|
||||
{unapprovedMessagesCount > 1 ? (
|
||||
<Button
|
||||
type="link"
|
||||
className="signature-request__reject-all-button"
|
||||
data-testid="signature-request-reject-all"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.handleCancelAll();
|
||||
}}
|
||||
>
|
||||
{rejectNText}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../shared/constants/app';
|
||||
|
||||
export { ENVIRONMENT_TYPE_NOTIFICATION };
|
@ -1,298 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
accountsWithSendEtherInfoSelector,
|
||||
doesAddressRequireLedgerHidConnection,
|
||||
getCurrentChainId,
|
||||
getRpcPrefsForCurrentProvider,
|
||||
getSubjectMetadata,
|
||||
unconfirmedMessagesHashSelector,
|
||||
getTotalUnapprovedMessagesCount,
|
||||
getCurrentCurrency,
|
||||
getPreferences,
|
||||
conversionRateSelector,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getAccountType,
|
||||
getSelectedAccount,
|
||||
unapprovedTypedMessagesSelector,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../selectors';
|
||||
import {
|
||||
isAddressLedger,
|
||||
getNativeCurrency,
|
||||
getProviderConfig,
|
||||
} from '../../../ducks/metamask/metamask';
|
||||
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
// eslint-disable-next-line import/order
|
||||
import { showCustodianDeepLink } from '@metamask-institutional/extension';
|
||||
import {
|
||||
mmiActionsFactory,
|
||||
setTypedMessageInProgress,
|
||||
} from '../../../store/institutional/institution-background';
|
||||
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
||||
import {
|
||||
showCustodyConfirmLink,
|
||||
checkForUnapprovedMessages,
|
||||
} from '../../../store/institutional/institution-actions';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../../shared/constants/app';
|
||||
|
||||
import {
|
||||
showModal,
|
||||
resolvePendingApproval,
|
||||
rejectPendingApproval,
|
||||
rejectAllMessages,
|
||||
completedTx,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
goHome,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../store/actions';
|
||||
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
|
||||
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
import SignatureRequest from './signature-request.component';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const { txData } = ownProps;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const envType = getEnvironmentType();
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
const {
|
||||
msgParams: { from },
|
||||
} = txData;
|
||||
const providerConfig = getProviderConfig(state);
|
||||
|
||||
const hardwareWalletRequiresConnection =
|
||||
doesAddressRequireLedgerHidConnection(state, from);
|
||||
const isLedgerWallet = isAddressLedger(state, from);
|
||||
const chainId = getCurrentChainId(state);
|
||||
const rpcPrefs = getRpcPrefsForCurrentProvider(state);
|
||||
const unconfirmedMessagesList = unconfirmedMessagesHashSelector(state);
|
||||
const unapprovedMessagesCount = getTotalUnapprovedMessagesCount(state);
|
||||
const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state);
|
||||
|
||||
return {
|
||||
providerConfig,
|
||||
isLedgerWallet,
|
||||
hardwareWalletRequiresConnection,
|
||||
chainId,
|
||||
rpcPrefs,
|
||||
unconfirmedMessagesList,
|
||||
unapprovedMessagesCount,
|
||||
mostRecentOverviewPage: getMostRecentOverviewPage(state),
|
||||
nativeCurrency: getNativeCurrency(state),
|
||||
currentCurrency: getCurrentCurrency(state),
|
||||
conversionRate: useNativeCurrencyAsPrimaryCurrency
|
||||
? null
|
||||
: conversionRateSelector(state),
|
||||
subjectMetadata: getSubjectMetadata(state),
|
||||
// not forwarded to component
|
||||
allAccounts: accountsWithSendEtherInfoSelector(state),
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
accountType: getAccountType(state),
|
||||
isNotification: envType === ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
selectedAccount: getSelectedAccount(state),
|
||||
unapprovedTypedMessages: unapprovedTypedMessagesSelector(state),
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
}
|
||||
|
||||
let mapDispatchToProps = null;
|
||||
|
||||
mapDispatchToProps = function (dispatch) {
|
||||
return {
|
||||
resolvePendingApproval: (id) => dispatch(resolvePendingApproval(id)),
|
||||
completedTx: (id) => dispatch(completedTx(id)),
|
||||
rejectPendingApproval: (id, error) =>
|
||||
dispatch(rejectPendingApproval(id, error)),
|
||||
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
||||
showRejectTransactionsConfirmationModal: ({
|
||||
onSubmit,
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
}) => {
|
||||
return dispatch(
|
||||
showModal({
|
||||
name: 'REJECT_TRANSACTIONS',
|
||||
onSubmit,
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
isRequestType: true,
|
||||
}),
|
||||
);
|
||||
},
|
||||
cancelAllApprovals: (unconfirmedMessagesList) => {
|
||||
dispatch(rejectAllMessages(unconfirmedMessagesList));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
function mmiMapDispatchToProps(dispatch) {
|
||||
const mmiActions = mmiActionsFactory();
|
||||
return {
|
||||
setMsgInProgress: (msgId) => dispatch(setTypedMessageInProgress(msgId)),
|
||||
showCustodianDeepLink: ({
|
||||
custodyId,
|
||||
fromAddress,
|
||||
closeNotification,
|
||||
onDeepLinkFetched,
|
||||
onDeepLinkShown,
|
||||
}) =>
|
||||
showCustodianDeepLink({
|
||||
dispatch,
|
||||
mmiActions,
|
||||
txId: undefined,
|
||||
fromAddress,
|
||||
custodyId,
|
||||
isSignature: true,
|
||||
closeNotification,
|
||||
onDeepLinkFetched,
|
||||
onDeepLinkShown,
|
||||
showCustodyConfirmLink,
|
||||
}),
|
||||
showTransactionsFailedModal: ({
|
||||
errorMessage,
|
||||
closeNotification,
|
||||
operationFailed,
|
||||
}) =>
|
||||
dispatch(
|
||||
showModal({
|
||||
name: 'TRANSACTION_FAILED',
|
||||
errorMessage,
|
||||
closeNotification,
|
||||
operationFailed,
|
||||
}),
|
||||
),
|
||||
setWaitForConfirmDeepLinkDialog: (wait) =>
|
||||
dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(wait)),
|
||||
goHome: () => dispatch(goHome()),
|
||||
resolvePendingApproval: (id) => dispatch(resolvePendingApproval(id)),
|
||||
completedTx: (id) => dispatch(completedTx(id)),
|
||||
rejectPendingApproval: (id, error) =>
|
||||
dispatch(rejectPendingApproval(id, error)),
|
||||
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
||||
showRejectTransactionsConfirmationModal: ({
|
||||
onSubmit,
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
}) => {
|
||||
return dispatch(
|
||||
showModal({
|
||||
name: 'REJECT_TRANSACTIONS',
|
||||
onSubmit,
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
isRequestType: true,
|
||||
}),
|
||||
);
|
||||
},
|
||||
cancelAllApprovals: (unconfirmedMessagesList) => {
|
||||
dispatch(rejectAllMessages(unconfirmedMessagesList));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
mapDispatchToProps = mmiMapDispatchToProps;
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
function mergeProps(stateProps, dispatchProps, ownProps) {
|
||||
const {
|
||||
allAccounts,
|
||||
isLedgerWallet,
|
||||
hardwareWalletRequiresConnection,
|
||||
chainId,
|
||||
rpcPrefs,
|
||||
nativeCurrency,
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
providerConfig,
|
||||
subjectMetadata,
|
||||
unconfirmedMessagesList,
|
||||
unapprovedMessagesCount,
|
||||
mostRecentOverviewPage,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
accountType,
|
||||
isNotification,
|
||||
unapprovedTypedMessages,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = stateProps;
|
||||
const { txData } = ownProps;
|
||||
|
||||
const {
|
||||
cancelAll: dispatchCancelAll,
|
||||
cancelAllApprovals: dispatchCancelAllApprovals,
|
||||
} = dispatchProps;
|
||||
|
||||
const {
|
||||
msgParams: { from },
|
||||
} = txData;
|
||||
|
||||
const fromAccount = getAccountByAddress(allAccounts, from);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const mmiOnSignCallback = async (_msgData) => {
|
||||
if (accountType === 'custody') {
|
||||
try {
|
||||
let msgData = _msgData;
|
||||
let id = _msgData.custodyId;
|
||||
if (!_msgData.custodyId) {
|
||||
msgData = checkForUnapprovedMessages(
|
||||
_msgData,
|
||||
unapprovedTypedMessages,
|
||||
);
|
||||
id = msgData.custodyId;
|
||||
}
|
||||
dispatchProps.showCustodianDeepLink({
|
||||
custodyId: id,
|
||||
fromAddress: fromAccount.address,
|
||||
closeNotification: isNotification,
|
||||
onDeepLinkFetched: () => undefined,
|
||||
onDeepLinkShown: () => undefined,
|
||||
});
|
||||
await dispatchProps.setMsgInProgress(msgData.metamaskId);
|
||||
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
|
||||
await goHome();
|
||||
} catch (err) {
|
||||
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
|
||||
await dispatchProps.showTransactionsFailedModal({
|
||||
errorMessage: err.message,
|
||||
closeNotification: true,
|
||||
operationFailed: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
...dispatchProps,
|
||||
fromAccount,
|
||||
txData,
|
||||
isLedgerWallet,
|
||||
hardwareWalletRequiresConnection,
|
||||
chainId,
|
||||
rpcPrefs,
|
||||
nativeCurrency,
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
providerConfig,
|
||||
subjectMetadata,
|
||||
unapprovedMessagesCount,
|
||||
mostRecentOverviewPage,
|
||||
cancelAll: () => dispatchCancelAll(valuesFor(unconfirmedMessagesList)),
|
||||
cancelAllApprovals: () =>
|
||||
dispatchCancelAllApprovals(valuesFor(unconfirmedMessagesList)),
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
mmiOnSignCallback,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps,
|
||||
)(SignatureRequest);
|
@ -1,237 +0,0 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { fireEvent, screen, act } from '@testing-library/react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import SignatureRequest from './signature-request.container';
|
||||
|
||||
const mockStoreWithEth = {
|
||||
metamask: {
|
||||
tokenList: {
|
||||
'0x514910771af9ca656af840dff83e8264ecf986ca': {
|
||||
address: '0x514910771af9ca656af840dff83e8264ecf986ca',
|
||||
symbol: 'LINK',
|
||||
decimals: 18,
|
||||
name: 'ChainLink Token',
|
||||
iconUrl: 'https://crypto.com/price/coin-data/icon/LINK/color_icon.png',
|
||||
aggregators: [
|
||||
'Aave',
|
||||
'Bancor',
|
||||
'CMC',
|
||||
'Crypto.com',
|
||||
'CoinGecko',
|
||||
'1inch',
|
||||
'Paraswap',
|
||||
'PMM',
|
||||
'Zapper',
|
||||
'Zerion',
|
||||
'0x',
|
||||
],
|
||||
occurrences: 12,
|
||||
unlisted: false,
|
||||
},
|
||||
},
|
||||
identities: {
|
||||
'0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e': {
|
||||
name: 'Account 2',
|
||||
address: '0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e',
|
||||
},
|
||||
},
|
||||
addressBook: {
|
||||
undefined: {
|
||||
0: {
|
||||
address: '0x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
|
||||
name: '',
|
||||
isEns: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
providerConfig: {
|
||||
type: 'rpc',
|
||||
},
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
accounts: {
|
||||
'0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5': {
|
||||
address: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
balance: '0x03',
|
||||
},
|
||||
},
|
||||
cachedBalances: {},
|
||||
unapprovedDecryptMsgs: {},
|
||||
unapprovedEncryptionPublicKeyMsgs: {},
|
||||
unconfirmedTransactions: {},
|
||||
selectedAddress: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
nativeCurrency: 'ETH',
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
};
|
||||
|
||||
const mockStoreWithFiat = {
|
||||
...mockStoreWithEth,
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: false,
|
||||
},
|
||||
};
|
||||
describe('Signature Request', () => {
|
||||
const propsWithEth = {
|
||||
fromAccount: {
|
||||
address: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
balance: '0x346ba7725f412cbfdb',
|
||||
name: 'John Doe',
|
||||
},
|
||||
history: {
|
||||
push: sinon.spy(),
|
||||
},
|
||||
hardwareWalletRequiresConnection: false,
|
||||
mostRecentOverviewPage: '/',
|
||||
clearConfirmTransaction: sinon.spy(),
|
||||
cancelMessage: sinon.spy(),
|
||||
cancel: sinon.stub().resolves(),
|
||||
rejectPendingApproval: sinon.stub().resolves(),
|
||||
showRejectTransactionsConfirmationModal: sinon.stub().resolves(),
|
||||
cancelAll: sinon.stub().resolves(),
|
||||
providerConfig: {
|
||||
type: 'rpc',
|
||||
},
|
||||
unapprovedMessagesCount: 2,
|
||||
sign: sinon.stub().resolves(),
|
||||
cancelAllApprovals: sinon.stub().resolves(),
|
||||
resolvePendingApproval: sinon.stub().resolves(),
|
||||
completedTx: sinon.stub().resolves(),
|
||||
txData: {
|
||||
msgParams: {
|
||||
id: 1,
|
||||
data: '{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"4","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","wallet":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},"contents":"Hello, Bob!"}}',
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
origin: 'test.domain',
|
||||
},
|
||||
status: 'unapproved',
|
||||
time: 1,
|
||||
type: 'eth_sign',
|
||||
},
|
||||
nativeCurrency: 'ETH',
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: null,
|
||||
selectedAccount: {
|
||||
address: '0x123456789abcdef',
|
||||
},
|
||||
};
|
||||
|
||||
const propsWithFiat = {
|
||||
...propsWithEth,
|
||||
conversionRate: 156.72,
|
||||
};
|
||||
|
||||
describe('Render with different currencies', () => {
|
||||
it('should render balance with ETH when conversionRate is not provided', () => {
|
||||
const store = configureMockStore()(mockStoreWithEth);
|
||||
renderWithProvider(
|
||||
<SignatureRequest.WrappedComponent {...propsWithEth} />,
|
||||
store,
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId('request-signature-account').textContent,
|
||||
).toMatchInlineSnapshot(
|
||||
`"UUnknown private networkJohn DoeBalance966.987986 ETH"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render balance with fiat when conversionRate not provided', () => {
|
||||
const store = configureMockStore()(mockStoreWithFiat);
|
||||
renderWithProvider(
|
||||
<SignatureRequest.WrappedComponent {...propsWithFiat} />,
|
||||
store,
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId('request-signature-account').textContent,
|
||||
).toMatchInlineSnapshot(
|
||||
`"UUnknown private networkJohn DoeBalance$151,546.36 USD"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('functionality check', () => {
|
||||
beforeEach(() => {
|
||||
const store = configureMockStore()(mockStoreWithFiat);
|
||||
renderWithProvider(
|
||||
<SignatureRequest.WrappedComponent {...propsWithFiat} />,
|
||||
store,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
propsWithFiat.clearConfirmTransaction.resetHistory();
|
||||
});
|
||||
|
||||
it('cancel', async () => {
|
||||
const cancelButton = screen.getByTestId('page-container-footer-cancel');
|
||||
await act(() => {
|
||||
fireEvent.click(cancelButton);
|
||||
});
|
||||
expect(propsWithFiat.rejectPendingApproval.calledOnce).toStrictEqual(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('sign', async () => {
|
||||
const signButton = screen.getByTestId('page-container-footer-next');
|
||||
await act(() => {
|
||||
fireEvent.click(signButton);
|
||||
});
|
||||
expect(propsWithFiat.resolvePendingApproval.calledOnce).toStrictEqual(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('have user warning', () => {
|
||||
const warningText = screen.getByText(
|
||||
'Only sign this message if you fully understand the content and trust the requesting site.',
|
||||
);
|
||||
|
||||
expect(warningText).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('contract details', () => {
|
||||
let store;
|
||||
beforeEach(() => {
|
||||
store = configureMockStore()(mockStoreWithFiat);
|
||||
});
|
||||
it('shows verify contract details link when verifyingContract is set', () => {
|
||||
renderWithProvider(
|
||||
<SignatureRequest.WrappedComponent {...propsWithFiat} />,
|
||||
store,
|
||||
);
|
||||
const verifyingContractLink = screen.getByTestId(
|
||||
'verify-contract-details',
|
||||
);
|
||||
expect(verifyingContractLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show verify contract details link when verifyingContract is not set', () => {
|
||||
const newData = JSON.parse(propsWithFiat.txData.msgParams.data);
|
||||
delete newData.domain.verifyingContract;
|
||||
|
||||
const newProps = {
|
||||
...propsWithFiat,
|
||||
txData: {
|
||||
...propsWithFiat.txData,
|
||||
msgParams: {
|
||||
...propsWithFiat.txData.msgParams,
|
||||
data: JSON.stringify(newData),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProvider(
|
||||
<SignatureRequest.WrappedComponent {...newProps} />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId('verify-contract-details')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
380
ui/components/app/signature-request/signature-request.js
Normal file
380
ui/components/app/signature-request/signature-request.js
Normal file
@ -0,0 +1,380 @@
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import {
|
||||
useDispatch,
|
||||
useSelector,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
shallowEqual,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { memoize } from 'lodash';
|
||||
import { ethErrors, serializeError } from 'eth-rpc-errors';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import { showCustodianDeepLink } from '@metamask-institutional/extension';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import {
|
||||
resolvePendingApproval,
|
||||
rejectPendingApproval,
|
||||
completedTx,
|
||||
} from '../../../store/actions';
|
||||
import {
|
||||
doesAddressRequireLedgerHidConnection,
|
||||
getSubjectMetadata,
|
||||
getTotalUnapprovedMessagesCount,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
accountsWithSendEtherInfoSelector,
|
||||
getSelectedAccount,
|
||||
getAccountType,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../selectors';
|
||||
import {
|
||||
getProviderConfig,
|
||||
isAddressLedger,
|
||||
} from '../../../ducks/metamask/metamask';
|
||||
import {
|
||||
sanitizeMessage,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getAccountByAddress,
|
||||
shortenAddress,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../helpers/utils/util';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { useRejectTransactionModal } from '../../../hooks/useRejectTransactionModal';
|
||||
|
||||
import { ConfirmPageContainerNavigation } from '../confirm-page-container';
|
||||
import SignatureRequestHeader from '../signature-request-header/signature-request-header';
|
||||
import SecurityProviderBannerMessage from '../security-provider-banner-message';
|
||||
import LedgerInstructionField from '../ledger-instruction-field';
|
||||
import ContractDetailsModal from '../modals/contract-details-modal';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import {
|
||||
MetaMetricsEventCategory,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
MetaMetricsEventName,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../../shared/constants/metametrics';
|
||||
import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../../../../shared/constants/security-provider';
|
||||
|
||||
import {
|
||||
TextAlign,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
Size,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
IconColor,
|
||||
BackgroundColor,
|
||||
Display,
|
||||
BlockSize,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import {
|
||||
BUTTON_VARIANT,
|
||||
Button,
|
||||
ButtonLink,
|
||||
TagUrl,
|
||||
Text,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
Icon,
|
||||
IconName,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../component-library';
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
// eslint-disable-next-line import/order
|
||||
import Box from '../../ui/box/box';
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../shared/constants/app';
|
||||
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
||||
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
|
||||
import { showCustodyConfirmLink } from '../../../store/institutional/institution-actions';
|
||||
import { useMMICustodySignMessage } from '../../../hooks/useMMICustodySignMessage';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
import Message from './signature-request-message';
|
||||
import Footer from './signature-request-footer';
|
||||
|
||||
const SignatureRequest = ({ txData }) => {
|
||||
const trackEvent = useContext(MetaMetricsContext);
|
||||
const dispatch = useDispatch();
|
||||
const t = useI18nContext();
|
||||
|
||||
const [hasScrolledMessage, setHasScrolledMessage] = useState(false);
|
||||
const [showContractDetails, setShowContractDetails] = useState(false);
|
||||
const [messageRootRef, setMessageRootRef] = useState(null);
|
||||
const [messageIsScrollable, setMessageIsScrollable] = useState(false);
|
||||
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
msgParams: { from, data, origin, version },
|
||||
} = txData;
|
||||
|
||||
// not forwarded to component
|
||||
const hardwareWalletRequiresConnection = useSelector((state) =>
|
||||
doesAddressRequireLedgerHidConnection(state, from),
|
||||
);
|
||||
const { chainId, rpcPrefs } = useSelector(getProviderConfig);
|
||||
const unapprovedMessagesCount = useSelector(getTotalUnapprovedMessagesCount);
|
||||
const subjectMetadata = useSelector(getSubjectMetadata);
|
||||
const isLedgerWallet = useSelector((state) => isAddressLedger(state, from));
|
||||
const { handleCancelAll } = useRejectTransactionModal();
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
// Used to show a warning if the signing account is not the selected account
|
||||
// Largely relevant for contract wallet custodians
|
||||
const selectedAccount = useSelector(getSelectedAccount);
|
||||
const mmiActions = mmiActionsFactory();
|
||||
const accountType = useSelector(getAccountType);
|
||||
const isNotification = getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION;
|
||||
const allAccounts = useSelector(
|
||||
accountsWithSendEtherInfoSelector,
|
||||
shallowEqual,
|
||||
);
|
||||
const { address } = getAccountByAddress(allAccounts, from) || {};
|
||||
const { custodySignFn } = useMMICustodySignMessage();
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
useEffect(() => {
|
||||
setMessageIsScrollable(
|
||||
messageRootRef?.scrollHeight > messageRootRef?.clientHeight,
|
||||
);
|
||||
}, [messageRootRef]);
|
||||
|
||||
const targetSubjectMetadata = subjectMetadata?.[origin] || null;
|
||||
|
||||
const parseMessage = memoize((dataToParse) => {
|
||||
const {
|
||||
message,
|
||||
domain = {},
|
||||
primaryType,
|
||||
types,
|
||||
} = JSON.parse(dataToParse);
|
||||
const sanitizedMessage = sanitizeMessage(message, primaryType, types);
|
||||
return { sanitizedMessage, domain, primaryType };
|
||||
});
|
||||
|
||||
const onSign = async () => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
if (accountType === 'custody') {
|
||||
await custodySignFn(txData);
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
await dispatch(resolvePendingApproval(id));
|
||||
completedTx(id);
|
||||
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Transactions,
|
||||
event: 'Confirm',
|
||||
properties: {
|
||||
action: 'Sign Request',
|
||||
legacy_event: true,
|
||||
type,
|
||||
version,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onCancel = async () => {
|
||||
await dispatch(
|
||||
rejectPendingApproval(
|
||||
id,
|
||||
serializeError(ethErrors.provider.userRejectedRequest()),
|
||||
),
|
||||
);
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Transactions,
|
||||
event: 'Cancel',
|
||||
properties: {
|
||||
action: 'Sign Request',
|
||||
legacy_event: true,
|
||||
type,
|
||||
version,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
sanitizedMessage,
|
||||
domain: { verifyingContract },
|
||||
primaryType,
|
||||
} = parseMessage(data);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
useEffect(() => {
|
||||
if (txData.custodyId) {
|
||||
showCustodianDeepLink({
|
||||
dispatch,
|
||||
mmiActions,
|
||||
txId: undefined,
|
||||
custodyId: txData.custodyId,
|
||||
fromAddress: address,
|
||||
isSignature: true,
|
||||
closeNotification: isNotification,
|
||||
onDeepLinkFetched: () => undefined,
|
||||
onDeepLinkShown: () => {
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.MMI,
|
||||
event: MetaMetricsEventName.SignatureDeeplinkDisplayed,
|
||||
});
|
||||
},
|
||||
showCustodyConfirmLink,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
dispatch,
|
||||
mmiActions,
|
||||
txData.custodyId,
|
||||
address,
|
||||
isNotification,
|
||||
trackEvent,
|
||||
]);
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
return (
|
||||
<div className="signature-request">
|
||||
<ConfirmPageContainerNavigation />
|
||||
<div
|
||||
className="request-signature__account"
|
||||
data-testid="request-signature-account"
|
||||
>
|
||||
<SignatureRequestHeader txData={txData} />
|
||||
</div>
|
||||
<div className="signature-request-content">
|
||||
{(txData?.securityProviderResponse?.flagAsDangerous !== undefined &&
|
||||
txData?.securityProviderResponse?.flagAsDangerous !==
|
||||
SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS) ||
|
||||
(txData?.securityProviderResponse &&
|
||||
Object.keys(txData.securityProviderResponse).length === 0) ? (
|
||||
<SecurityProviderBannerMessage
|
||||
securityProviderResponse={txData.securityProviderResponse}
|
||||
/>
|
||||
) : null}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
selectedAccount.address === address ? null : (
|
||||
<Box
|
||||
className="request-signature__mismatch-info"
|
||||
display={Display.Flex}
|
||||
width={BlockSize.Full}
|
||||
padding={4}
|
||||
marginBottom={4}
|
||||
backgroundColor={BackgroundColor.primaryMuted}
|
||||
>
|
||||
<Icon
|
||||
name={IconName.Info}
|
||||
color={IconColor.infoDefault}
|
||||
marginRight={2}
|
||||
/>
|
||||
<Text
|
||||
variant={TextVariant.bodyXs}
|
||||
color={TextColor.textDefault}
|
||||
as="h6"
|
||||
>
|
||||
{t('mismatchAccount', [
|
||||
shortenAddress(selectedAccount.address),
|
||||
shortenAddress(address),
|
||||
])}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
<div className="signature-request__origin">
|
||||
<TagUrl
|
||||
label={origin}
|
||||
labelProps={{
|
||||
color: TextColor.textAlternative,
|
||||
}}
|
||||
src={targetSubjectMetadata?.iconUrl}
|
||||
/>
|
||||
</div>
|
||||
<Text
|
||||
className="signature-request__content__title"
|
||||
variant={TextVariant.headingLg}
|
||||
marginTop={4}
|
||||
>
|
||||
{t('sigRequest')}
|
||||
</Text>
|
||||
<Text
|
||||
className="request-signature__content__subtitle"
|
||||
variant={TextVariant.bodySm}
|
||||
color={TextColor.textAlternative}
|
||||
textAlign={TextAlign.Center}
|
||||
marginLeft={12}
|
||||
marginRight={12}
|
||||
marginTop={4}
|
||||
as="h6"
|
||||
>
|
||||
{t('signatureRequestGuidance')}
|
||||
</Text>
|
||||
{verifyingContract ? (
|
||||
<div>
|
||||
<Button
|
||||
variant={BUTTON_VARIANT.LINK}
|
||||
onClick={() => setShowContractDetails(true)}
|
||||
className="signature-request-content__verify-contract-details"
|
||||
data-testid="verify-contract-details"
|
||||
>
|
||||
<Text
|
||||
variant={TextVariant.bodySm}
|
||||
color={TextColor.primaryDefault}
|
||||
as="h6"
|
||||
>
|
||||
{t('verifyContractDetails')}
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{isLedgerWallet ? (
|
||||
<div className="confirm-approve-content__ledger-instruction-wrapper">
|
||||
<LedgerInstructionField showDataInstruction />
|
||||
</div>
|
||||
) : null}
|
||||
<Message
|
||||
data={sanitizedMessage}
|
||||
onMessageScrolled={() => setHasScrolledMessage(true)}
|
||||
setMessageRootRef={setMessageRootRef}
|
||||
messageRootRef={messageRootRef}
|
||||
messageIsScrollable={messageIsScrollable}
|
||||
primaryType={primaryType}
|
||||
/>
|
||||
<Footer
|
||||
cancelAction={onCancel}
|
||||
signAction={onSign}
|
||||
disabled={
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
Boolean(txData?.custodyId) ||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
hardwareWalletRequiresConnection ||
|
||||
(messageIsScrollable && !hasScrolledMessage)
|
||||
}
|
||||
/>
|
||||
{showContractDetails && (
|
||||
<ContractDetailsModal
|
||||
toAddress={verifyingContract}
|
||||
chainId={chainId}
|
||||
rpcPrefs={rpcPrefs}
|
||||
onClose={() => setShowContractDetails(false)}
|
||||
isContractRequestingSignature
|
||||
/>
|
||||
)}
|
||||
{unapprovedMessagesCount > 1 ? (
|
||||
<ButtonLink
|
||||
size={Size.inherit}
|
||||
className="signature-request__reject-all-button"
|
||||
data-testid="signature-request-reject-all"
|
||||
onClick={handleCancelAll}
|
||||
>
|
||||
{t('rejectRequestsN', [unapprovedMessagesCount])}
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SignatureRequest.propTypes = {
|
||||
txData: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SignatureRequest;
|
@ -1,14 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from '../../../store/store';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import README from './README.mdx';
|
||||
import SignatureRequest from './signature-request.component';
|
||||
import SignatureRequest from './signature-request';
|
||||
|
||||
const [MOCK_PRIMARY_IDENTITY, MOCK_SECONDARY_IDENTITY] = Object.values(
|
||||
testData.metamask.identities,
|
||||
);
|
||||
const store = configureStore({
|
||||
...testData,
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
selectedAddress: '0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e',
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
title: 'Components/App/SignatureRequest',
|
||||
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||
|
||||
component: SignatureRequest,
|
||||
parameters: {
|
||||
docs: {
|
||||
@ -17,22 +25,6 @@ export default {
|
||||
},
|
||||
argTypes: {
|
||||
txData: { control: 'object' },
|
||||
fromAccount: {
|
||||
table: {
|
||||
address: { control: 'text' },
|
||||
balance: { control: 'text' },
|
||||
name: { control: 'text' },
|
||||
},
|
||||
},
|
||||
hardwareWalletRequiresConnection: { control: 'boolean' },
|
||||
isLedgerWallet: { control: 'boolean' },
|
||||
clearConfirmTransaction: { action: 'Clean Confirm' },
|
||||
cancel: { action: 'Cancel' },
|
||||
sign: { action: 'Sign' },
|
||||
showRejectTransactionsConfirmationModal: {
|
||||
action: 'showRejectTransactionsConfirmationModal',
|
||||
},
|
||||
cancelAll: { action: 'cancelAll' },
|
||||
},
|
||||
};
|
||||
|
||||
@ -45,6 +37,7 @@ DefaultStory.storyName = 'Default';
|
||||
DefaultStory.args = {
|
||||
txData: {
|
||||
msgParams: {
|
||||
from: '0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e',
|
||||
data: JSON.stringify({
|
||||
domain: {
|
||||
name: 'happydapp.website',
|
||||
@ -79,11 +72,6 @@ DefaultStory.args = {
|
||||
origin: 'https://happydapp.website/',
|
||||
},
|
||||
},
|
||||
fromAccount: MOCK_PRIMARY_IDENTITY,
|
||||
providerConfig: { name: 'Goerli ETH' },
|
||||
selectedAccount: MOCK_PRIMARY_IDENTITY,
|
||||
hardwareWalletRequiresConnection: false,
|
||||
currentCurrency: 'usd',
|
||||
};
|
||||
|
||||
export const AccountMismatchStory = (args) => {
|
||||
@ -94,5 +82,10 @@ AccountMismatchStory.storyName = 'AccountMismatch';
|
||||
|
||||
AccountMismatchStory.args = {
|
||||
...DefaultStory.args,
|
||||
selectedAccount: MOCK_SECONDARY_IDENTITY,
|
||||
txData: {
|
||||
msgParams: {
|
||||
...DefaultStory.args.txData.msgParams,
|
||||
from: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,33 +1,98 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../../../../shared/constants/security-provider';
|
||||
import SignatureRequest from './signature-request.component';
|
||||
import {
|
||||
getNativeCurrency,
|
||||
getProviderConfig,
|
||||
} from '../../../ducks/metamask/metamask';
|
||||
import {
|
||||
accountsWithSendEtherInfoSelector,
|
||||
conversionRateSelector,
|
||||
getCurrentCurrency,
|
||||
getMemoizedAddressBook,
|
||||
getMemoizedMetaMaskIdentities,
|
||||
getPreferences,
|
||||
getSelectedAccount,
|
||||
getTotalUnapprovedMessagesCount,
|
||||
unconfirmedTransactionsHashSelector,
|
||||
} from '../../../selectors';
|
||||
import SignatureRequest from './signature-request';
|
||||
|
||||
const baseProps = {
|
||||
hardwareWalletRequiresConnection: false,
|
||||
clearConfirmTransaction: () => undefined,
|
||||
cancel: () => undefined,
|
||||
cancelAll: () => undefined,
|
||||
mostRecentOverviewPage: '/',
|
||||
showRejectTransactionsConfirmationModal: () => undefined,
|
||||
sign: () => undefined,
|
||||
history: { push: '/' },
|
||||
providerConfig: { type: 'rpc' },
|
||||
nativeCurrency: 'ABC',
|
||||
currentCurrency: 'def',
|
||||
fromAccount: {
|
||||
address: '0x123456789abcdef',
|
||||
balance: '0x346ba7725f412cbfdb',
|
||||
name: 'Antonio',
|
||||
},
|
||||
selectedAccount: {
|
||||
address: '0x123456789abcdef',
|
||||
clearConfirmTransaction: () => jest.fn(),
|
||||
cancel: () => jest.fn(),
|
||||
cancelAll: () => jest.fn(),
|
||||
showRejectTransactionsConfirmationModal: () => jest.fn(),
|
||||
sign: () => jest.fn(),
|
||||
};
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
providerConfig: {
|
||||
chainId: '0x539',
|
||||
nickname: 'Localhost 8545',
|
||||
rpcPrefs: {},
|
||||
rpcUrl: 'http://localhost:8545',
|
||||
ticker: 'ETH',
|
||||
type: 'rpc',
|
||||
},
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
accounts: {
|
||||
'0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5': {
|
||||
address: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
balance: '0x03',
|
||||
name: 'John Doe',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
nativeCurrency: 'ETH',
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: null,
|
||||
unapprovedTypedMessagesCount: 2,
|
||||
},
|
||||
};
|
||||
jest.mock('react-redux', () => {
|
||||
const actual = jest.requireActual('react-redux');
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useSelector: jest.fn(),
|
||||
useDispatch: () => jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const generateUseSelectorRouter = (opts) => (selector) => {
|
||||
switch (selector) {
|
||||
case getProviderConfig:
|
||||
return opts.metamask.providerConfig;
|
||||
case getCurrentCurrency:
|
||||
return opts.metamask.currentCurrency;
|
||||
case getNativeCurrency:
|
||||
return opts.metamask.nativeCurrency;
|
||||
case getTotalUnapprovedMessagesCount:
|
||||
return opts.metamask.unapprovedTypedMessagesCount;
|
||||
case getPreferences:
|
||||
return opts.metamask.preferences;
|
||||
case conversionRateSelector:
|
||||
return opts.metamask.conversionRate;
|
||||
case getSelectedAccount:
|
||||
return opts.metamask.accounts[opts.metamask.selectedAddress];
|
||||
case getMemoizedAddressBook:
|
||||
return [];
|
||||
case accountsWithSendEtherInfoSelector:
|
||||
return Object.values(opts.metamask.accounts);
|
||||
case unconfirmedTransactionsHashSelector:
|
||||
case getMemoizedMetaMaskIdentities:
|
||||
return {};
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
describe('Signature Request Component', () => {
|
||||
const store = configureMockStore()(mockState);
|
||||
|
||||
@ -35,6 +100,7 @@ describe('Signature Request Component', () => {
|
||||
let messageData;
|
||||
|
||||
beforeEach(() => {
|
||||
useSelector.mockImplementation(generateUseSelectorRouter(mockStore));
|
||||
messageData = {
|
||||
domain: {
|
||||
chainId: 97,
|
||||
@ -84,7 +150,17 @@ describe('Signature Request Component', () => {
|
||||
});
|
||||
|
||||
it('should match snapshot when we want to switch to fiat', () => {
|
||||
useSelector.mockImplementation(
|
||||
generateUseSelectorRouter({
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -95,7 +171,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={1567}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -105,6 +180,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should match snapshot when we are using eth', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -115,7 +191,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -125,6 +200,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should render navigation', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -135,7 +211,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -149,6 +224,7 @@ describe('Signature Request Component', () => {
|
||||
do_not_display: 'two',
|
||||
};
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -159,7 +235,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -171,7 +246,17 @@ describe('Signature Request Component', () => {
|
||||
});
|
||||
|
||||
it('should not render a reject multiple requests link if there is not multiple requests', () => {
|
||||
useSelector.mockImplementation(
|
||||
generateUseSelectorRouter({
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
unapprovedTypedMessagesCount: 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -182,7 +267,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -194,6 +278,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should render a reject multiple requests link if there is multiple requests (greater than 1)', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -204,8 +289,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -217,6 +300,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should call reject all button when button is clicked', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -227,8 +311,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -242,6 +324,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should render text of reject all button', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -252,8 +335,6 @@ describe('Signature Request Component', () => {
|
||||
txData={{
|
||||
msgParams,
|
||||
}}
|
||||
conversionRate={null}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -263,6 +344,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should render SecurityProviderBannerMessage component properly', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -271,7 +353,6 @@ describe('Signature Request Component', () => {
|
||||
const { queryByText } = renderWithProvider(
|
||||
<SignatureRequest
|
||||
{...baseProps}
|
||||
conversionRate={null}
|
||||
txData={{
|
||||
msgParams,
|
||||
securityProviderResponse: {
|
||||
@ -280,7 +361,6 @@ describe('Signature Request Component', () => {
|
||||
reason_header: 'Some reason header...',
|
||||
},
|
||||
}}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -296,6 +376,7 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should not render SecurityProviderBannerMessage component when flagAsDangerous is not malicious', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
@ -304,14 +385,12 @@ describe('Signature Request Component', () => {
|
||||
const { queryByText } = renderWithProvider(
|
||||
<SignatureRequest
|
||||
{...baseProps}
|
||||
conversionRate={null}
|
||||
txData={{
|
||||
msgParams,
|
||||
securityProviderResponse: {
|
||||
flagAsDangerous: SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS,
|
||||
},
|
||||
}}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
@ -327,27 +406,39 @@ describe('Signature Request Component', () => {
|
||||
|
||||
it('should render a warning when the selected account is not the one being used to sign', () => {
|
||||
const msgParams = {
|
||||
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||
data: JSON.stringify(messageData),
|
||||
version: 'V4',
|
||||
origin: 'test',
|
||||
};
|
||||
|
||||
useSelector.mockImplementation(
|
||||
generateUseSelectorRouter({
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
selectedAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||
accounts: {
|
||||
...mockStore.metamask.accounts,
|
||||
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
|
||||
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||
balance: '0x0',
|
||||
name: 'Account 1',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const { container } = renderWithProvider(
|
||||
<SignatureRequest
|
||||
{...baseProps}
|
||||
selectedAccount={{
|
||||
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||
balance: '0x0',
|
||||
name: 'Account 1',
|
||||
}}
|
||||
conversionRate={null}
|
||||
txData={{
|
||||
msgParams,
|
||||
securityProviderResponse: {
|
||||
flagAsDangerous: SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS,
|
||||
},
|
||||
}}
|
||||
unapprovedMessagesCount={2}
|
||||
/>,
|
||||
store,
|
||||
);
|
77
ui/hooks/useMMICustodySignMessage.js
Normal file
77
ui/hooks/useMMICustodySignMessage.js
Normal file
@ -0,0 +1,77 @@
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { showCustodianDeepLink } from '@metamask-institutional/extension';
|
||||
import {
|
||||
showCustodyConfirmLink,
|
||||
checkForUnapprovedMessages,
|
||||
} from '../store/institutional/institution-actions';
|
||||
import {
|
||||
mmiActionsFactory,
|
||||
setTypedMessageInProgress,
|
||||
} from '../store/institutional/institution-background';
|
||||
import {
|
||||
accountsWithSendEtherInfoSelector,
|
||||
getAccountType,
|
||||
unapprovedTypedMessagesSelector,
|
||||
} from '../selectors';
|
||||
import { getAccountByAddress } from '../helpers/utils/util';
|
||||
import { getEnvironmentType } from '../../app/scripts/lib/util';
|
||||
import { goHome, showModal } from '../store/actions';
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../shared/constants/app';
|
||||
|
||||
export function useMMICustodySignMessage() {
|
||||
const dispatch = useDispatch();
|
||||
const mmiActions = mmiActionsFactory();
|
||||
const envType = getEnvironmentType();
|
||||
const accountType = useSelector(getAccountType);
|
||||
const isNotification = envType === ENVIRONMENT_TYPE_NOTIFICATION;
|
||||
const allAccounts = useSelector(
|
||||
accountsWithSendEtherInfoSelector,
|
||||
shallowEqual,
|
||||
);
|
||||
const unapprovedTypedMessages = useSelector(unapprovedTypedMessagesSelector);
|
||||
|
||||
const custodySignFn = async (_msgData) => {
|
||||
if (accountType === 'custody') {
|
||||
const { address: fromAddress } =
|
||||
getAccountByAddress(allAccounts, _msgData.msgParams.from) || {};
|
||||
try {
|
||||
let msgData = _msgData;
|
||||
let id = _msgData.custodyId;
|
||||
if (!_msgData.custodyId) {
|
||||
msgData = checkForUnapprovedMessages(
|
||||
_msgData,
|
||||
unapprovedTypedMessages,
|
||||
);
|
||||
id = msgData.custodyId;
|
||||
}
|
||||
showCustodianDeepLink({
|
||||
dispatch,
|
||||
mmiActions,
|
||||
txId: undefined,
|
||||
custodyId: id,
|
||||
fromAddress,
|
||||
isSignature: true,
|
||||
closeNotification: isNotification,
|
||||
onDeepLinkFetched: () => undefined,
|
||||
onDeepLinkShown: () => undefined,
|
||||
showCustodyConfirmLink,
|
||||
});
|
||||
await dispatch(setTypedMessageInProgress(msgData.metamaskId));
|
||||
await dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(true));
|
||||
await dispatch(goHome());
|
||||
} catch (err) {
|
||||
await dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(true));
|
||||
await dispatch(
|
||||
showModal({
|
||||
name: 'TRANSACTION_FAILED',
|
||||
errorMessage: err.message,
|
||||
closeNotification: true,
|
||||
operationFailed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { custodySignFn };
|
||||
}
|
35
ui/hooks/useRejectTransactionModal.js
Normal file
35
ui/hooks/useRejectTransactionModal.js
Normal file
@ -0,0 +1,35 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { valuesFor } from '../helpers/utils/util';
|
||||
import { showModal, rejectAllMessages } from '../store/actions';
|
||||
import { clearConfirmTransaction } from '../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
import { getMostRecentOverviewPage } from '../ducks/history/history';
|
||||
import {
|
||||
getTotalUnapprovedMessagesCount,
|
||||
unconfirmedMessagesHashSelector,
|
||||
} from '../selectors';
|
||||
|
||||
export function useRejectTransactionModal() {
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage);
|
||||
const unapprovedMessagesCount = useSelector(getTotalUnapprovedMessagesCount);
|
||||
const unconfirmedMessagesList = useSelector(unconfirmedMessagesHashSelector);
|
||||
|
||||
const handleCancelAll = () => {
|
||||
dispatch(
|
||||
showModal({
|
||||
name: 'REJECT_TRANSACTIONS',
|
||||
onSubmit: async () => {
|
||||
await dispatch(rejectAllMessages(valuesFor(unconfirmedMessagesList)));
|
||||
dispatch(clearConfirmTransaction());
|
||||
history.push(mostRecentOverviewPage);
|
||||
},
|
||||
unapprovedTxCount: unapprovedMessagesCount,
|
||||
isRequestType: true,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return { handleCancelAll };
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Signature Request Component render should match snapshot 1`] = `
|
||||
exports[`Confirm Signature Request Component render should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="signature-request"
|
||||
@ -178,56 +178,48 @@ exports[`Signature Request Component render should match snapshot 1`] = `
|
||||
class="signature-request__origin"
|
||||
>
|
||||
<div
|
||||
class="site-origin"
|
||||
class="box mm-tag-url box--padding-right-4 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--align-items-center box--background-color-background-default box--rounded-pill box--border-color-border-default box--border-width-1 box--border-style-solid"
|
||||
>
|
||||
<div
|
||||
class="chip chip--with-left-icon chip--border-color-border-muted chip--background-color-undefined"
|
||||
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-avatar-favicon mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-transparent box--border-style-solid box--border-width-1"
|
||||
>
|
||||
<div
|
||||
class="chip__left-icon"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="icon-with-fallback__fallback"
|
||||
>
|
||||
M
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||
>
|
||||
https://metamask.github.io
|
||||
</span>
|
||||
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-default"
|
||||
style="mask-image: url('./images/icons/global.svg');"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="mm-box mm-text mm-text--body-md mm-text--ellipsis mm-box--color-text-alternative"
|
||||
>
|
||||
https://metamask.github.io
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-md mm-box--margin-top-4 mm-box--color-text-default"
|
||||
<h2
|
||||
class="mm-box mm-text signature-request__content__title mm-text--heading-lg mm-box--margin-top-4 mm-box--color-text-default"
|
||||
>
|
||||
Signature request
|
||||
</h3>
|
||||
</h2>
|
||||
<h6
|
||||
align="center"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-box--margin-12 mm-box--margin-top-3 mm-box--color-text-alternative"
|
||||
class="mm-box mm-text request-signature__content__subtitle mm-text--body-sm mm-text--text-align-center mm-box--margin-top-4 mm-box--margin-right-12 mm-box--margin-left-12 mm-box--color-text-alternative"
|
||||
>
|
||||
Only sign this message if you fully understand the content and trust the requesting site.
|
||||
</h6>
|
||||
<div>
|
||||
<a
|
||||
class="button btn-link signature-request-content__verify-contract-details"
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base signature-request-content__verify-contract-details mm-button-link mm-button-link--size-auto mm-text--body-md-medium mm-box--padding-right-0 mm-box--padding-left-0 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent"
|
||||
data-testid="verify-contract-details"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
<span
|
||||
class="mm-box mm-text mm-text--inherit mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</a>
|
||||
<h6
|
||||
class="mm-box mm-text mm-text--body-sm mm-box--color-primary-default"
|
||||
>
|
||||
Verify third-party details
|
||||
</h6>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -57,7 +57,7 @@ const mockState = {
|
||||
send: { draftTransactions: {} },
|
||||
};
|
||||
|
||||
describe('Signature Request Component', () => {
|
||||
describe('Confirm Signature Request Component', () => {
|
||||
const store = configureMockStore()(mockState);
|
||||
|
||||
describe('render', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user