2023-03-01 09:45:27 +01:00
|
|
|
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
2021-02-04 19:15:23 +01:00
|
|
|
import PropTypes from 'prop-types';
|
2023-01-26 18:07:17 +01:00
|
|
|
import { useSelector } from 'react-redux';
|
2021-11-29 17:08:24 +01:00
|
|
|
|
2023-01-27 19:28:03 +01:00
|
|
|
import { EditGasModes } from '../../../../shared/constants/gas';
|
2021-11-10 03:07:10 +01:00
|
|
|
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
2022-09-21 16:15:34 +02:00
|
|
|
import {
|
2023-01-18 15:47:29 +01:00
|
|
|
TokenStandard,
|
|
|
|
TransactionType,
|
2022-09-21 16:15:34 +02:00
|
|
|
} from '../../../../shared/constants/transaction';
|
2022-03-25 15:02:08 +01:00
|
|
|
import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network';
|
2021-11-29 17:08:24 +01:00
|
|
|
|
|
|
|
import { PageContainerFooter } from '../../ui/page-container';
|
2022-02-23 16:03:01 +01:00
|
|
|
import Button from '../../ui/button';
|
|
|
|
import ActionableMessage from '../../ui/actionable-message/actionable-message';
|
2021-11-29 17:08:24 +01:00
|
|
|
import SenderToRecipient from '../../ui/sender-to-recipient';
|
|
|
|
|
2021-11-23 18:46:33 +01:00
|
|
|
import AdvancedGasFeePopover from '../advanced-gas-fee-popover';
|
2021-11-29 17:08:24 +01:00
|
|
|
import EditGasFeePopover from '../edit-gas-fee-popover/edit-gas-fee-popover';
|
|
|
|
import EditGasPopover from '../edit-gas-popover';
|
2022-02-23 16:03:01 +01:00
|
|
|
import ErrorMessage from '../../ui/error-message';
|
|
|
|
import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys';
|
2023-03-29 00:04:22 +02:00
|
|
|
import { Text } from '../../component-library';
|
|
|
|
import {
|
|
|
|
TextVariant,
|
|
|
|
TEXT_ALIGN,
|
|
|
|
} from '../../../helpers/constants/design-system';
|
2021-11-29 17:08:24 +01:00
|
|
|
|
2022-09-21 16:15:34 +02:00
|
|
|
import NetworkAccountBalanceHeader from '../network-account-balance-header/network-account-balance-header';
|
2023-03-08 18:35:45 +01:00
|
|
|
import { fetchTokenBalance } from '../../../../shared/lib/token-util.ts';
|
2022-12-06 21:24:07 +01:00
|
|
|
import SetApproveForAllWarning from '../set-approval-for-all-warning';
|
2023-01-26 18:07:17 +01:00
|
|
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
2023-02-01 06:54:41 +01:00
|
|
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
|
|
import useTransactionInsights from '../../../hooks/useTransactionInsights';
|
|
|
|
///: END:ONLY_INCLUDE_IN(flask)
|
2023-01-26 18:07:17 +01:00
|
|
|
import {
|
|
|
|
getAccountName,
|
|
|
|
getAddressBookEntry,
|
|
|
|
getIsBuyableChain,
|
|
|
|
getMetadataContractName,
|
|
|
|
getMetaMaskIdentities,
|
|
|
|
getNetworkIdentifier,
|
|
|
|
getSwapsDefaultToken,
|
|
|
|
} from '../../../selectors';
|
2023-03-01 09:45:27 +01:00
|
|
|
import useRamps from '../../../hooks/experiences/useRamps';
|
|
|
|
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
2023-04-03 17:31:04 +02:00
|
|
|
import {
|
|
|
|
MetaMetricsEventCategory,
|
|
|
|
MetaMetricsEventName,
|
|
|
|
} from '../../../../shared/constants/metametrics';
|
2020-11-03 00:41:28 +01:00
|
|
|
import {
|
|
|
|
ConfirmPageContainerHeader,
|
|
|
|
ConfirmPageContainerContent,
|
|
|
|
ConfirmPageContainerNavigation,
|
2021-02-04 19:15:23 +01:00
|
|
|
} from '.';
|
2018-06-23 08:52:45 +02:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const ConfirmPageContainer = (props) => {
|
|
|
|
const {
|
|
|
|
showEdit,
|
|
|
|
onEdit,
|
|
|
|
fromName,
|
|
|
|
fromAddress,
|
|
|
|
toEns,
|
|
|
|
toNickname,
|
|
|
|
toAddress,
|
|
|
|
disabled,
|
|
|
|
errorKey,
|
|
|
|
errorMessage,
|
|
|
|
contentComponent,
|
|
|
|
action,
|
|
|
|
title,
|
|
|
|
image,
|
|
|
|
titleComponent,
|
|
|
|
subtitleComponent,
|
|
|
|
detailsComponent,
|
|
|
|
dataComponent,
|
|
|
|
dataHexComponent,
|
|
|
|
onCancelAll,
|
|
|
|
onCancel,
|
|
|
|
onSubmit,
|
|
|
|
onSetApprovalForAll,
|
|
|
|
showWarningModal,
|
|
|
|
tokenAddress,
|
|
|
|
nonce,
|
|
|
|
unapprovedTxCount,
|
|
|
|
warning,
|
|
|
|
hideSenderToRecipient,
|
|
|
|
showAccountInHeader,
|
|
|
|
origin,
|
|
|
|
ethGasPriceWarning,
|
|
|
|
editingGas,
|
|
|
|
handleCloseEditGas,
|
|
|
|
currentTransaction,
|
|
|
|
supportsEIP1559,
|
|
|
|
nativeCurrency,
|
2023-02-01 06:54:41 +01:00
|
|
|
txData,
|
2023-01-26 18:07:17 +01:00
|
|
|
assetStandard,
|
|
|
|
isApprovalOrRejection,
|
|
|
|
} = props;
|
2018-06-23 08:52:45 +02:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const t = useI18nContext();
|
2023-03-01 09:45:27 +01:00
|
|
|
const trackEvent = useContext(MetaMetricsContext);
|
2022-12-06 21:24:07 +01:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const [collectionBalance, setCollectionBalance] = useState(0);
|
2022-12-06 21:24:07 +01:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const isBuyableChain = useSelector(getIsBuyableChain);
|
|
|
|
const contact = useSelector((state) => getAddressBookEntry(state, toAddress));
|
|
|
|
const networkIdentifier = useSelector(getNetworkIdentifier);
|
|
|
|
const defaultToken = useSelector(getSwapsDefaultToken);
|
|
|
|
const accountBalance = defaultToken.string;
|
|
|
|
const identities = useSelector(getMetaMaskIdentities);
|
|
|
|
const ownedAccountName = getAccountName(identities, toAddress);
|
|
|
|
const toName = ownedAccountName || contact?.name;
|
|
|
|
const recipientIsOwnedAccount = Boolean(ownedAccountName);
|
|
|
|
const toMetadataName = useSelector((state) =>
|
|
|
|
getMetadataContractName(state, toAddress),
|
|
|
|
);
|
2018-06-23 08:52:45 +02:00
|
|
|
|
2023-03-01 09:45:27 +01:00
|
|
|
// TODO: Move useRamps hook to the confirm-transaction-base parent component.
|
|
|
|
// TODO: openBuyCryptoInPdapp should be passed to this component as a custom prop.
|
|
|
|
// We try to keep this component for layout purpose only, we need to move this hook to the confirm-transaction-base parent
|
|
|
|
// component once it is converted to a functional component
|
|
|
|
const { openBuyCryptoInPdapp } = useRamps();
|
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const isSetApproveForAll =
|
|
|
|
currentTransaction.type === TransactionType.tokenMethodSetApprovalForAll;
|
2021-11-03 16:13:31 +01:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const shouldDisplayWarning =
|
|
|
|
contentComponent && disabled && (errorKey || errorMessage);
|
2021-11-05 10:12:23 +01:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const networkName =
|
|
|
|
NETWORK_TO_NAME_MAP[currentTransaction.chainId] || networkIdentifier;
|
2022-08-10 16:54:44 +02:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
const fetchCollectionBalance = useCallback(async () => {
|
2023-03-08 18:35:45 +01:00
|
|
|
const tokenBalance = await fetchTokenBalance(
|
|
|
|
tokenAddress,
|
|
|
|
fromAddress,
|
|
|
|
global.ethereumProvider,
|
|
|
|
);
|
2023-01-26 18:07:17 +01:00
|
|
|
setCollectionBalance(tokenBalance?.balance?.words?.[0] || 0);
|
|
|
|
}, [fromAddress, tokenAddress]);
|
2022-11-09 16:36:21 +01:00
|
|
|
|
2023-02-01 06:54:41 +01:00
|
|
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
|
|
// As confirm-transction-base is converted to functional component
|
|
|
|
// this code can bemoved to it.
|
|
|
|
const insightComponent = useTransactionInsights({
|
|
|
|
txData,
|
|
|
|
});
|
|
|
|
///: END:ONLY_INCLUDE_IN
|
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
useEffect(() => {
|
|
|
|
if (isSetApproveForAll && assetStandard === TokenStandard.ERC721) {
|
|
|
|
fetchCollectionBalance();
|
|
|
|
}
|
|
|
|
}, [
|
|
|
|
currentTransaction,
|
|
|
|
assetStandard,
|
|
|
|
isSetApproveForAll,
|
|
|
|
fetchCollectionBalance,
|
|
|
|
collectionBalance,
|
|
|
|
]);
|
2022-02-23 16:03:01 +01:00
|
|
|
|
2023-01-26 18:07:17 +01:00
|
|
|
return (
|
|
|
|
<GasFeeContextProvider transaction={currentTransaction}>
|
|
|
|
<div className="page-container" data-testid="page-container">
|
|
|
|
<ConfirmPageContainerNavigation />
|
|
|
|
{assetStandard === TokenStandard.ERC20 ||
|
|
|
|
assetStandard === TokenStandard.ERC721 ||
|
|
|
|
assetStandard === TokenStandard.ERC1155 ? (
|
|
|
|
<NetworkAccountBalanceHeader
|
|
|
|
accountName={fromName}
|
|
|
|
accountBalance={accountBalance}
|
|
|
|
tokenName={nativeCurrency}
|
|
|
|
accountAddress={fromAddress}
|
|
|
|
networkName={networkName}
|
|
|
|
chainId={currentTransaction.chainId}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<ConfirmPageContainerHeader
|
|
|
|
showEdit={showEdit}
|
|
|
|
onEdit={() => onEdit()}
|
|
|
|
showAccountInHeader={showAccountInHeader}
|
|
|
|
accountAddress={fromAddress}
|
|
|
|
>
|
|
|
|
{hideSenderToRecipient ? null : (
|
|
|
|
<SenderToRecipient
|
|
|
|
senderName={fromName}
|
|
|
|
senderAddress={fromAddress}
|
|
|
|
recipientName={toName}
|
|
|
|
recipientMetadataName={toMetadataName}
|
|
|
|
recipientAddress={toAddress}
|
|
|
|
recipientEns={toEns}
|
|
|
|
recipientNickname={toNickname}
|
|
|
|
recipientIsOwnedAccount={recipientIsOwnedAccount}
|
2022-03-25 15:02:08 +01:00
|
|
|
/>
|
2023-01-26 18:07:17 +01:00
|
|
|
)}
|
|
|
|
</ConfirmPageContainerHeader>
|
|
|
|
)}
|
|
|
|
{contentComponent || (
|
|
|
|
<ConfirmPageContainerContent
|
|
|
|
action={action}
|
|
|
|
title={title}
|
|
|
|
image={image}
|
|
|
|
titleComponent={titleComponent}
|
|
|
|
subtitleComponent={subtitleComponent}
|
|
|
|
detailsComponent={detailsComponent}
|
|
|
|
dataComponent={dataComponent}
|
|
|
|
dataHexComponent={dataHexComponent}
|
|
|
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
|
|
insightComponent={insightComponent}
|
|
|
|
///: END:ONLY_INCLUDE_IN
|
|
|
|
errorMessage={errorMessage}
|
|
|
|
errorKey={errorKey}
|
|
|
|
tokenAddress={tokenAddress}
|
|
|
|
nonce={nonce}
|
|
|
|
warning={warning}
|
|
|
|
onCancelAll={onCancelAll}
|
|
|
|
onCancel={onCancel}
|
|
|
|
cancelText={t('reject')}
|
|
|
|
onSubmit={onSubmit}
|
|
|
|
submitText={t('confirm')}
|
|
|
|
disabled={disabled}
|
|
|
|
unapprovedTxCount={unapprovedTxCount}
|
|
|
|
rejectNText={t('rejectTxsN', [unapprovedTxCount])}
|
|
|
|
origin={origin}
|
|
|
|
ethGasPriceWarning={ethGasPriceWarning}
|
|
|
|
supportsEIP1559={supportsEIP1559}
|
|
|
|
currentTransaction={currentTransaction}
|
|
|
|
nativeCurrency={nativeCurrency}
|
|
|
|
networkName={networkName}
|
|
|
|
toAddress={toAddress}
|
|
|
|
transactionType={currentTransaction.type}
|
|
|
|
isBuyableChain={isBuyableChain}
|
2023-02-23 12:38:09 +01:00
|
|
|
txData={txData}
|
2023-01-26 18:07:17 +01:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{shouldDisplayWarning && errorKey === INSUFFICIENT_FUNDS_ERROR_KEY && (
|
|
|
|
<div className="confirm-approve-content__warning">
|
|
|
|
<ActionableMessage
|
|
|
|
message={
|
|
|
|
isBuyableChain ? (
|
2023-03-29 00:04:22 +02:00
|
|
|
<Text
|
|
|
|
variant={TextVariant.bodySm}
|
|
|
|
textAlign={TEXT_ALIGN.LEFT}
|
|
|
|
as="h6"
|
|
|
|
>
|
2023-01-26 18:07:17 +01:00
|
|
|
{t('insufficientCurrencyBuyOrDeposit', [
|
|
|
|
nativeCurrency,
|
|
|
|
networkName,
|
|
|
|
<Button
|
|
|
|
type="inline"
|
|
|
|
className="confirm-page-container-content__link"
|
2023-03-01 09:45:27 +01:00
|
|
|
onClick={() => {
|
|
|
|
openBuyCryptoInPdapp();
|
|
|
|
trackEvent({
|
2023-04-03 17:31:04 +02:00
|
|
|
event: MetaMetricsEventName.NavBuyButtonClicked,
|
|
|
|
category: MetaMetricsEventCategory.Navigation,
|
2023-03-01 09:45:27 +01:00
|
|
|
properties: {
|
|
|
|
location: 'Transaction Confirmation',
|
|
|
|
text: 'Buy',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}}
|
2023-01-26 18:07:17 +01:00
|
|
|
key={`${nativeCurrency}-buy-button`}
|
|
|
|
>
|
|
|
|
{t('buyAsset', [nativeCurrency])}
|
|
|
|
</Button>,
|
|
|
|
])}
|
2023-03-29 00:04:22 +02:00
|
|
|
</Text>
|
2023-01-26 18:07:17 +01:00
|
|
|
) : (
|
2023-03-29 00:04:22 +02:00
|
|
|
<Text
|
|
|
|
variant={TextVariant.bodySm}
|
|
|
|
textAlign={TEXT_ALIGN.LEFT}
|
|
|
|
as="h6"
|
|
|
|
>
|
2023-01-26 18:07:17 +01:00
|
|
|
{t('insufficientCurrencyDeposit', [
|
|
|
|
nativeCurrency,
|
|
|
|
networkName,
|
|
|
|
])}
|
2023-03-29 00:04:22 +02:00
|
|
|
</Text>
|
2023-01-26 18:07:17 +01:00
|
|
|
)
|
2022-12-16 18:07:18 +01:00
|
|
|
}
|
2023-01-26 18:07:17 +01:00
|
|
|
useIcon
|
|
|
|
iconFillColor="var(--color-error-default)"
|
|
|
|
type="danger"
|
2021-11-10 03:07:10 +01:00
|
|
|
/>
|
2023-01-26 18:07:17 +01:00
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{shouldDisplayWarning && errorKey !== INSUFFICIENT_FUNDS_ERROR_KEY && (
|
|
|
|
<div className="confirm-approve-content__warning">
|
|
|
|
<ErrorMessage errorKey={errorKey} />
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{showWarningModal && (
|
|
|
|
<SetApproveForAllWarning
|
|
|
|
collectionName={title}
|
|
|
|
senderAddress={fromAddress}
|
|
|
|
name={fromName}
|
|
|
|
isERC721={assetStandard === TokenStandard.ERC20}
|
|
|
|
total={collectionBalance}
|
|
|
|
onSubmit={onSubmit}
|
|
|
|
onCancel={onCancel}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{contentComponent && (
|
|
|
|
<PageContainerFooter
|
|
|
|
onCancel={onCancel}
|
|
|
|
cancelText={t('reject')}
|
|
|
|
onSubmit={
|
|
|
|
isSetApproveForAll && isApprovalOrRejection
|
|
|
|
? onSetApprovalForAll
|
|
|
|
: onSubmit
|
|
|
|
}
|
|
|
|
submitText={t('confirm')}
|
2023-02-01 02:22:33 +01:00
|
|
|
submitButtonType={
|
|
|
|
isSetApproveForAll && isApprovalOrRejection
|
|
|
|
? 'danger-primary'
|
|
|
|
: 'primary'
|
|
|
|
}
|
2023-01-26 18:07:17 +01:00
|
|
|
disabled={disabled}
|
|
|
|
>
|
|
|
|
{unapprovedTxCount > 1 && (
|
|
|
|
<a onClick={onCancelAll}>
|
|
|
|
{t('rejectTxsN', [unapprovedTxCount])}
|
|
|
|
</a>
|
|
|
|
)}
|
|
|
|
</PageContainerFooter>
|
|
|
|
)}
|
|
|
|
{editingGas && !supportsEIP1559 && (
|
|
|
|
<EditGasPopover
|
2023-01-27 19:28:03 +01:00
|
|
|
mode={EditGasModes.modifyInPlace}
|
2023-01-26 18:07:17 +01:00
|
|
|
onClose={handleCloseEditGas}
|
|
|
|
transaction={currentTransaction}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{supportsEIP1559 && (
|
|
|
|
<>
|
|
|
|
<EditGasFeePopover />
|
|
|
|
<AdvancedGasFeePopover />
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</GasFeeContextProvider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
ConfirmPageContainer.propTypes = {
|
|
|
|
// Header
|
|
|
|
action: PropTypes.string,
|
|
|
|
onEdit: PropTypes.func,
|
|
|
|
showEdit: PropTypes.bool,
|
|
|
|
subtitleComponent: PropTypes.node,
|
|
|
|
title: PropTypes.string,
|
|
|
|
image: PropTypes.string,
|
|
|
|
titleComponent: PropTypes.node,
|
|
|
|
hideSenderToRecipient: PropTypes.bool,
|
|
|
|
showAccountInHeader: PropTypes.bool,
|
|
|
|
assetStandard: PropTypes.string,
|
|
|
|
// Sender to Recipient
|
|
|
|
fromAddress: PropTypes.string,
|
|
|
|
fromName: PropTypes.string,
|
|
|
|
toAddress: PropTypes.string,
|
|
|
|
toEns: PropTypes.string,
|
|
|
|
toNickname: PropTypes.string,
|
|
|
|
// Content
|
|
|
|
contentComponent: PropTypes.node,
|
|
|
|
errorKey: PropTypes.string,
|
|
|
|
errorMessage: PropTypes.string,
|
|
|
|
dataComponent: PropTypes.node,
|
|
|
|
dataHexComponent: PropTypes.node,
|
|
|
|
detailsComponent: PropTypes.node,
|
2023-02-01 06:54:41 +01:00
|
|
|
txData: PropTypes.object,
|
2023-01-26 18:07:17 +01:00
|
|
|
tokenAddress: PropTypes.string,
|
|
|
|
nonce: PropTypes.string,
|
|
|
|
warning: PropTypes.string,
|
|
|
|
unapprovedTxCount: PropTypes.number,
|
|
|
|
origin: PropTypes.string.isRequired,
|
|
|
|
ethGasPriceWarning: PropTypes.string,
|
|
|
|
// Footer
|
|
|
|
onCancelAll: PropTypes.func,
|
|
|
|
onCancel: PropTypes.func,
|
|
|
|
onSubmit: PropTypes.func,
|
|
|
|
onSetApprovalForAll: PropTypes.func,
|
|
|
|
showWarningModal: PropTypes.bool,
|
|
|
|
disabled: PropTypes.bool,
|
|
|
|
editingGas: PropTypes.bool,
|
|
|
|
handleCloseEditGas: PropTypes.func,
|
|
|
|
// Gas Popover
|
|
|
|
currentTransaction: PropTypes.object.isRequired,
|
|
|
|
supportsEIP1559: PropTypes.bool,
|
|
|
|
nativeCurrency: PropTypes.string,
|
|
|
|
isApprovalOrRejection: PropTypes.bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
export default ConfirmPageContainer;
|