1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Display a warning and gas fee component for token allowance and NFT flow when transaction is expected to fail (#17437)

Co-authored-by: Pedro Figueiredo <pedro.figueiredo@consensys.net>
Co-authored-by: Dan J Miller <danjm.com@gmail.com>
Co-authored-by: Brad Decker <bhdecker84@gmail.com>
This commit is contained in:
Vladimir Saric 2023-02-02 00:14:09 +01:00 committed by GitHub
parent fd819451e1
commit 308151cd49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 369 additions and 42 deletions

View File

@ -43,6 +43,8 @@ export default function ApproveContentCard({
isSetApproveForAll, isSetApproveForAll,
isApprovalOrRejection, isApprovalOrRejection,
data, data,
userAcknowledgedGasMissing,
renderSimulationFailureWarning,
}) { }) {
const t = useContext(I18nContext); const t = useContext(I18nContext);
@ -91,9 +93,14 @@ export default function ApproveContentCard({
</Button> </Button>
</Box> </Box>
)} )}
{showEdit && showAdvanceGasFeeOptions && supportsEIP1559 && ( {showEdit &&
<EditGasFeeButton /> showAdvanceGasFeeOptions &&
)} supportsEIP1559 &&
!renderSimulationFailureWarning && (
<EditGasFeeButton
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
)}
</Box> </Box>
)} )}
<Box <Box
@ -102,8 +109,12 @@ export default function ApproveContentCard({
className="approve-content-card-container__card-content" className="approve-content-card-container__card-content"
> >
{renderTransactionDetailsContent && {renderTransactionDetailsContent &&
(!isMultiLayerFeeNetwork && supportsEIP1559 ? ( (!isMultiLayerFeeNetwork &&
<GasDetailsItem /> supportsEIP1559 &&
!renderSimulationFailureWarning ? (
<GasDetailsItem
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
) : ( ) : (
<Box <Box
display={DISPLAY.FLEX} display={DISPLAY.FLEX}
@ -301,4 +312,12 @@ ApproveContentCard.propTypes = {
* Current transaction data * Current transaction data
*/ */
data: PropTypes.string, data: PropTypes.string,
/**
* User acknowledge gas is missing or not
*/
userAcknowledgedGasMissing: PropTypes.bool,
/**
* Render simulation failure warning
*/
renderSimulationFailureWarning: PropTypes.bool,
}; };

View File

@ -7,6 +7,7 @@ import { submittedPendingTransactionsSelector } from '../../../selectors';
import { useGasFeeContext } from '../../../contexts/gasFee'; import { useGasFeeContext } from '../../../contexts/gasFee';
import { useI18nContext } from '../../../hooks/useI18nContext'; import { useI18nContext } from '../../../hooks/useI18nContext';
import ActionableMessage from '../../ui/actionable-message/actionable-message'; import ActionableMessage from '../../ui/actionable-message/actionable-message';
import SimulationErrorMessage from '../../ui/simulation-error-message';
import Typography from '../../ui/typography'; import Typography from '../../ui/typography';
import { TYPOGRAPHY } from '../../../helpers/constants/design-system'; import { TYPOGRAPHY } from '../../../helpers/constants/design-system';
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
@ -23,19 +24,9 @@ const TransactionAlerts = ({
return ( return (
<div className="transaction-alerts"> <div className="transaction-alerts">
{supportsEIP1559 && hasSimulationError && ( {supportsEIP1559 && hasSimulationError && (
<ActionableMessage <SimulationErrorMessage
message={t('simulationErrorMessageV2')} userAcknowledgedGasMissing={userAcknowledgedGasMissing}
useIcon setUserAcknowledgedGasMissing={setUserAcknowledgedGasMissing}
iconFillColor="var(--color-error-default)"
type="danger"
primaryActionV2={
userAcknowledgedGasMissing === true
? undefined
: {
label: t('proceedWithTransaction'),
onClick: setUserAcknowledgedGasMissing,
}
}
/> />
)} )}
{supportsEIP1559 && pendingTransactions?.length > 0 && ( {supportsEIP1559 && pendingTransactions?.length > 0 && (

View File

@ -0,0 +1 @@
export { default } from './simulation-error-message';

View File

@ -0,0 +1,33 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import ActionableMessage from '../actionable-message';
import { I18nContext } from '../../../../.storybook/i18n';
export default function SimulationErrorMessage({
userAcknowledgedGasMissing = false,
setUserAcknowledgedGasMissing,
}) {
const t = useContext(I18nContext);
return (
<ActionableMessage
message={t('simulationErrorMessageV2')}
useIcon
iconFillColor="var(--color-error-default)"
type="danger"
primaryActionV2={
userAcknowledgedGasMissing === true
? undefined
: {
label: t('proceedWithTransaction'),
onClick: setUserAcknowledgedGasMissing,
}
}
/>
);
}
SimulationErrorMessage.propTypes = {
userAcknowledgedGasMissing: PropTypes.bool,
setUserAcknowledgedGasMissing: PropTypes.func,
};

View File

@ -0,0 +1,65 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { fireEvent } from '@testing-library/react';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import SimulationErrorMessage from './simulation-error-message';
describe('Simulation Error Message', () => {
const store = configureMockStore()({});
let props = {};
beforeEach(() => {
props = {
userAcknowledgedGasMissing: false,
setUserAcknowledgedGasMissing: jest.fn(),
};
});
it('should render SimulationErrorMessage component with I want to procced anyway link', () => {
const { queryByText } = renderWithProvider(
<SimulationErrorMessage {...props} />,
store,
);
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).toBeInTheDocument();
});
it('should render SimulationErrorMessage component without I want to procced anyway link', () => {
props.userAcknowledgedGasMissing = true;
const { queryByText } = renderWithProvider(
<SimulationErrorMessage {...props} />,
store,
);
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).not.toBeInTheDocument();
});
it('should render SimulationErrorMessage component with I want to proceed anyway and fire that event', () => {
props.userAcknowledgedGasMissing = false;
const { queryByText, getByText } = renderWithProvider(
<SimulationErrorMessage {...props} />,
store,
);
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).toBeInTheDocument();
const proceedAnywayLink = getByText('I want to proceed anyway');
fireEvent.click(proceedAnywayLink);
expect(props.setUserAcknowledgedGasMissing).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,18 @@
import { useSelector } from 'react-redux';
import { txDataSelector } from '../selectors';
/**
* Returns the simulation failure warning if a simulaiton error
* is present and user didn't acknowledge gas missing
*
* @param {boolean} userAcknowledgedGasMissing - Whether the user acknowledge gas missing
* @returns {boolean} simulation failure warning
*/
export function useSimulationFailureWarning(userAcknowledgedGasMissing) {
const txData = useSelector(txDataSelector) || {};
const hasSimulationError = Boolean(txData.simulationFails);
const renderSimulationFailureWarning =
hasSimulationError && !userAcknowledgedGasMissing;
return renderSimulationFailureWarning;
}

View File

@ -9,6 +9,7 @@ import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import Typography from '../../../components/ui/typography'; import Typography from '../../../components/ui/typography';
import Box from '../../../components/ui/box'; import Box from '../../../components/ui/box';
import Button from '../../../components/ui/button'; import Button from '../../../components/ui/button';
import SimulationErrorMessage from '../../../components/ui/simulation-error-message';
import EditGasFeeButton from '../../../components/app/edit-gas-fee-button'; import EditGasFeeButton from '../../../components/app/edit-gas-fee-button';
import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message'; import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message';
import { import {
@ -65,12 +66,15 @@ export default class ConfirmApproveContent extends Component {
isSetApproveForAll: PropTypes.bool, isSetApproveForAll: PropTypes.bool,
isApprovalOrRejection: PropTypes.bool, isApprovalOrRejection: PropTypes.bool,
userAddress: PropTypes.string, userAddress: PropTypes.string,
userAcknowledgedGasMissing: PropTypes.bool,
setUserAcknowledgedGasMissing: PropTypes.func,
renderSimulationFailureWarning: PropTypes.bool,
}; };
state = { state = {
showFullTxDetails: false, showFullTxDetails: false,
copied: false, copied: false,
setshowContractDetails: false, setShowContractDetails: false,
}; };
renderApproveContentCard({ renderApproveContentCard({
@ -84,7 +88,11 @@ export default class ConfirmApproveContent extends Component {
footer, footer,
noBorder, noBorder,
}) { }) {
const { supportsEIP1559 } = this.props; const {
supportsEIP1559,
renderSimulationFailureWarning,
userAcknowledgedGasMissing,
} = this.props;
const { t } = this.context; const { t } = this.context;
return ( return (
<div <div
@ -116,9 +124,14 @@ export default class ConfirmApproveContent extends Component {
</Button> </Button>
</Box> </Box>
)} )}
{showEdit && showAdvanceGasFeeOptions && supportsEIP1559 && ( {showEdit &&
<EditGasFeeButton /> showAdvanceGasFeeOptions &&
)} supportsEIP1559 &&
!renderSimulationFailureWarning && (
<EditGasFeeButton
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
)}
</div> </div>
)} )}
<div className="confirm-approve-content__card-content">{content}</div> <div className="confirm-approve-content__card-content">{content}</div>
@ -139,9 +152,19 @@ export default class ConfirmApproveContent extends Component {
txData, txData,
isMultiLayerFeeNetwork, isMultiLayerFeeNetwork,
supportsEIP1559, supportsEIP1559,
userAcknowledgedGasMissing,
renderSimulationFailureWarning,
} = this.props; } = this.props;
if (!isMultiLayerFeeNetwork && supportsEIP1559) { if (
return <GasDetailsItem />; !isMultiLayerFeeNetwork &&
supportsEIP1559 &&
!renderSimulationFailureWarning
) {
return (
<GasDetailsItem
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
);
} }
return ( return (
<div className="confirm-approve-content__transaction-details-content"> <div className="confirm-approve-content__transaction-details-content">
@ -491,8 +514,11 @@ export default class ConfirmApproveContent extends Component {
tokenId, tokenId,
tokenAddress, tokenAddress,
assetName, assetName,
userAcknowledgedGasMissing,
setUserAcknowledgedGasMissing,
renderSimulationFailureWarning,
} = this.props; } = this.props;
const { showFullTxDetails, setshowContractDetails } = this.state; const { showFullTxDetails, setShowContractDetails } = this.state;
return ( return (
<div <div
@ -539,13 +565,13 @@ export default class ConfirmApproveContent extends Component {
<Button <Button
type="link" type="link"
className="confirm-approve-content__verify-contract-details" className="confirm-approve-content__verify-contract-details"
onClick={() => this.setState({ setshowContractDetails: true })} onClick={() => this.setState({ setShowContractDetails: true })}
> >
{t('verifyContractDetails')} {t('verifyContractDetails')}
</Button> </Button>
{setshowContractDetails && ( {setShowContractDetails && (
<ContractDetailsModal <ContractDetailsModal
onClose={() => this.setState({ setshowContractDetails: false })} onClose={() => this.setState({ setShowContractDetails: false })}
tokenName={tokenSymbol} tokenName={tokenSymbol}
tokenAddress={tokenAddress} tokenAddress={tokenAddress}
toAddress={toAddress} toAddress={toAddress}
@ -558,6 +584,21 @@ export default class ConfirmApproveContent extends Component {
)} )}
</Box> </Box>
<div className="confirm-approve-content__card-wrapper"> <div className="confirm-approve-content__card-wrapper">
{renderSimulationFailureWarning && (
<Box
paddingTop={0}
paddingRight={6}
paddingBottom={4}
paddingLeft={6}
>
<SimulationErrorMessage
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
setUserAcknowledgedGasMissing={() =>
setUserAcknowledgedGasMissing(true)
}
/>
</Box>
)}
{this.renderApproveContentCard({ {this.renderApproveContentCard({
symbol: <i className="fa fa-tag" />, symbol: <i className="fa fa-tag" />,
title: t('transactionFee'), title: t('transactionFee'),

View File

@ -28,6 +28,10 @@ const props = {
useNonceField: true, useNonceField: true,
nextNonce: 1, nextNonce: 1,
customNonceValue: '2', customNonceValue: '2',
txData: { simulationFails: null },
userAcknowledgedGasMissing: false,
setUserAcknowledgedGasMissing: jest.fn(),
renderSimulationFailureWarning: false,
showCustomizeNonceModal: jest.fn(), showCustomizeNonceModal: jest.fn(),
chainId: '1337', chainId: '1337',
rpcPrefs: {}, rpcPrefs: {},
@ -50,6 +54,12 @@ describe('ConfirmApproveContent Component', () => {
), ),
).toBeInTheDocument(); ).toBeInTheDocument();
expect(queryByText('Verify contract details')).toBeInTheDocument(); expect(queryByText('Verify contract details')).toBeInTheDocument();
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).not.toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).not.toBeInTheDocument();
expect(queryByText('View full transaction details')).toBeInTheDocument(); expect(queryByText('View full transaction details')).toBeInTheDocument();
const editButtons = getAllByText('Edit'); const editButtons = getAllByText('Edit');
@ -86,4 +96,124 @@ describe('ConfirmApproveContent Component', () => {
), ),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
it('should render Confirm approve page correctly and simulation error message without I want to procced anyway link', () => {
props.userAcknowledgedGasMissing = true;
props.renderSimulationFailureWarning = true;
const { queryByText, getByText, getAllByText, getByTestId } =
renderComponent(props);
expect(
queryByText('https://metamask.github.io/test-dapp/'),
).toBeInTheDocument();
expect(getByTestId('confirm-approve-title').textContent).toStrictEqual(
' Allow access to and transfer of your TestDappCollectibles (#1)? ',
);
expect(
queryByText(
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
),
).toBeInTheDocument();
expect(queryByText('Verify contract details')).toBeInTheDocument();
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).not.toBeInTheDocument();
expect(queryByText('View full transaction details')).toBeInTheDocument();
const editButtons = getAllByText('Edit');
expect(queryByText('Transaction fee')).toBeInTheDocument();
expect(
queryByText('A fee is associated with this request.'),
).toBeInTheDocument();
expect(queryByText(`${props.ethTransactionTotal} ETH`)).toBeInTheDocument();
fireEvent.click(editButtons[0]);
expect(props.showCustomizeGasModal).toHaveBeenCalledTimes(2);
expect(queryByText('Nonce')).toBeInTheDocument();
expect(queryByText('2')).toBeInTheDocument();
fireEvent.click(editButtons[1]);
expect(props.showCustomizeNonceModal).toHaveBeenCalledTimes(2);
const showViewTxDetails = getByText('View full transaction details');
expect(queryByText('Permission request')).not.toBeInTheDocument();
expect(queryByText('Approved asset:')).not.toBeInTheDocument();
expect(queryByText('Granted to:')).not.toBeInTheDocument();
expect(queryByText('Data')).not.toBeInTheDocument();
fireEvent.click(showViewTxDetails);
expect(getByText('Hide full transaction details')).toBeInTheDocument();
expect(getByText('Permission request')).toBeInTheDocument();
expect(getByText('Approved asset:')).toBeInTheDocument();
expect(getByText('Granted to:')).toBeInTheDocument();
expect(getByText('Contract (0x9bc5baF8...fEF4)')).toBeInTheDocument();
expect(getByText('Data')).toBeInTheDocument();
expect(getByText('Function: Approve')).toBeInTheDocument();
expect(
getByText(
'0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
),
).toBeInTheDocument();
});
it('should render Confirm approve page correctly and simulation error message with I want to procced anyway link', () => {
props.userAcknowledgedGasMissing = false;
props.renderSimulationFailureWarning = true;
const { queryByText, getByText, getAllByText, getByTestId } =
renderComponent(props);
expect(
queryByText('https://metamask.github.io/test-dapp/'),
).toBeInTheDocument();
expect(getByTestId('confirm-approve-title').textContent).toStrictEqual(
' Allow access to and transfer of your TestDappCollectibles (#1)? ',
);
expect(
queryByText(
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
),
).toBeInTheDocument();
expect(queryByText('Verify contract details')).toBeInTheDocument();
expect(
queryByText(
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
),
).toBeInTheDocument();
expect(queryByText('I want to proceed anyway')).toBeInTheDocument();
expect(queryByText('View full transaction details')).toBeInTheDocument();
const editButtons = getAllByText('Edit');
expect(queryByText('Transaction fee')).toBeInTheDocument();
expect(
queryByText('A fee is associated with this request.'),
).toBeInTheDocument();
expect(queryByText(`${props.ethTransactionTotal} ETH`)).toBeInTheDocument();
fireEvent.click(editButtons[0]);
expect(props.showCustomizeGasModal).toHaveBeenCalledTimes(3);
expect(queryByText('Nonce')).toBeInTheDocument();
expect(queryByText('2')).toBeInTheDocument();
fireEvent.click(editButtons[1]);
expect(props.showCustomizeNonceModal).toHaveBeenCalledTimes(3);
const showViewTxDetails = getByText('View full transaction details');
expect(queryByText('Permission request')).not.toBeInTheDocument();
expect(queryByText('Approved asset:')).not.toBeInTheDocument();
expect(queryByText('Granted to:')).not.toBeInTheDocument();
expect(queryByText('Data')).not.toBeInTheDocument();
fireEvent.click(showViewTxDetails);
expect(getByText('Hide full transaction details')).toBeInTheDocument();
expect(getByText('Permission request')).toBeInTheDocument();
expect(getByText('Approved asset:')).toBeInTheDocument();
expect(getByText('Granted to:')).toBeInTheDocument();
expect(getByText('Contract (0x9bc5baF8...fEF4)')).toBeInTheDocument();
expect(getByText('Data')).toBeInTheDocument();
expect(getByText('Function: Approve')).toBeInTheDocument();
expect(
getByText(
'0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
),
).toBeInTheDocument();
});
}); });

View File

@ -129,6 +129,7 @@ export default {
useNonceField: true, useNonceField: true,
nextNonce: 1, nextNonce: 1,
customNonceValue: '2', customNonceValue: '2',
txData: { simulationFails: null },
chainId: '1337', chainId: '1337',
rpcPrefs: {}, rpcPrefs: {},
isContract: true, isContract: true,

View File

@ -29,6 +29,7 @@ import {
checkNetworkAndAccountSupports1559, checkNetworkAndAccountSupports1559,
} from '../../selectors'; } from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction'; import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { useSimulationFailureWarning } from '../../hooks/useSimulationFailureWarning';
import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover'; import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover';
import EditGasFeePopover from '../../components/app/edit-gas-fee-popover'; import EditGasFeePopover from '../../components/app/edit-gas-fee-popover';
import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component'; import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
@ -83,6 +84,8 @@ export default function ConfirmApprove({
const [customPermissionAmount, setCustomPermissionAmount] = useState(''); const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const [submitWarning, setSubmitWarning] = useState(''); const [submitWarning, setSubmitWarning] = useState('');
const [isContract, setIsContract] = useState(false); const [isContract, setIsContract] = useState(false);
const [userAcknowledgedGasMissing, setUserAcknowledgedGasMissing] =
useState(false);
const supportsEIP1559 = networkAndAccountSupports1559; const supportsEIP1559 = networkAndAccountSupports1559;
@ -92,6 +95,9 @@ export default function ConfirmApprove({
showCustomizeGasPopover, showCustomizeGasPopover,
closeCustomizeGasPopover, closeCustomizeGasPopover,
} = useApproveTransaction(); } = useApproveTransaction();
const renderSimulationFailureWarning = useSimulationFailureWarning(
userAcknowledgedGasMissing,
);
useEffect(() => { useEffect(() => {
if (customPermissionAmount && previousTokenAmount.current !== tokenAmount) { if (customPermissionAmount && previousTokenAmount.current !== tokenAmount) {
@ -246,6 +252,9 @@ export default function ConfirmApprove({
useNonceField={useNonceField} useNonceField={useNonceField}
nextNonce={nextNonce} nextNonce={nextNonce}
customNonceValue={customNonceValue} customNonceValue={customNonceValue}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
setUserAcknowledgedGasMissing={setUserAcknowledgedGasMissing}
renderSimulationFailureWarning={renderSimulationFailureWarning}
updateCustomNonce={(value) => { updateCustomNonce={(value) => {
dispatch(updateCustomNonce(value)); dispatch(updateCustomNonce(value));
}} }}

View File

@ -15,7 +15,7 @@ import CopyRawData from '../../components/app/transaction-decoding/components/ui
import { PRIMARY, SECONDARY } from '../../helpers/constants/common'; import { PRIMARY, SECONDARY } from '../../helpers/constants/common';
import TextField from '../../components/ui/text-field'; import TextField from '../../components/ui/text-field';
import ActionableMessage from '../../components/ui/actionable-message'; import SimulationErrorMessage from '../../components/ui/simulation-error-message';
import Disclosure from '../../components/ui/disclosure'; import Disclosure from '../../components/ui/disclosure';
import { EVENT } from '../../../shared/constants/metametrics'; import { EVENT } from '../../../shared/constants/metametrics';
import { import {
@ -563,18 +563,10 @@ export default class ConfirmTransactionBase extends Component {
const simulationFailureWarning = () => ( const simulationFailureWarning = () => (
<div className="confirm-page-container-content__error-container"> <div className="confirm-page-container-content__error-container">
<ActionableMessage <SimulationErrorMessage
message={t('simulationErrorMessageV2')} userAcknowledgedGasMissing={userAcknowledgedGasMissing}
useIcon setUserAcknowledgedGasMissing={() =>
iconFillColor="var(--color-error-default)" this.setUserAcknowledgedGasMissing()
type="danger"
primaryActionV2={
userAcknowledgedGasMissing === true
? undefined
: {
label: t('proceedWithTransaction'),
onClick: () => this.setUserAcknowledgedGasMissing(),
}
} }
/> />
</div> </div>

View File

@ -57,6 +57,8 @@ import {
NUM_W_OPT_DECIMAL_COMMA_OR_DOT_REGEX, NUM_W_OPT_DECIMAL_COMMA_OR_DOT_REGEX,
} from '../../../shared/constants/tokens'; } from '../../../shared/constants/tokens';
import { ConfirmPageContainerNavigation } from '../../components/app/confirm-page-container'; import { ConfirmPageContainerNavigation } from '../../components/app/confirm-page-container';
import { useSimulationFailureWarning } from '../../hooks/useSimulationFailureWarning';
import SimulationErrorMessage from '../../components/ui/simulation-error-message';
export default function TokenAllowance({ export default function TokenAllowance({
origin, origin,
@ -93,7 +95,12 @@ export default function TokenAllowance({
dappProposedTokenAmount !== '0', dappProposedTokenAmount !== '0',
); );
const [errorText, setErrorText] = useState(''); const [errorText, setErrorText] = useState('');
const [userAcknowledgedGasMissing, setUserAcknowledgedGasMissing] =
useState(false);
const renderSimulationFailureWarning = useSimulationFailureWarning(
userAcknowledgedGasMissing,
);
const currentAccount = useSelector(getCurrentAccountWithSendEtherInfo); const currentAccount = useSelector(getCurrentAccountWithSendEtherInfo);
const networkIdentifier = useSelector(getNetworkIdentifier); const networkIdentifier = useSelector(getNetworkIdentifier);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
@ -391,6 +398,21 @@ export default function TokenAllowance({
)} )}
{!isFirstPage && ( {!isFirstPage && (
<Box className="token-allowance-container__card-wrapper"> <Box className="token-allowance-container__card-wrapper">
{renderSimulationFailureWarning && (
<Box
paddingTop={0}
paddingRight={4}
paddingBottom={4}
paddingLeft={4}
>
<SimulationErrorMessage
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
setUserAcknowledgedGasMissing={() =>
setUserAcknowledgedGasMissing(true)
}
/>
</Box>
)}
<ApproveContentCard <ApproveContentCard
symbol={<i className="fa fa-tag" />} symbol={<i className="fa fa-tag" />}
title={t('transactionFee')} title={t('transactionFee')}
@ -404,6 +426,8 @@ export default function TokenAllowance({
ethTransactionTotal={ethTransactionTotal} ethTransactionTotal={ethTransactionTotal}
nativeCurrency={nativeCurrency} nativeCurrency={nativeCurrency}
fullTxData={fullTxData} fullTxData={fullTxData}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
renderSimulationFailureWarning={renderSimulationFailureWarning}
hexTransactionTotal={hexTransactionTotal} hexTransactionTotal={hexTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal} fiatTransactionTotal={fiatTransactionTotal}
currentCurrency={currentCurrency} currentCurrency={currentCurrency}
@ -449,6 +473,9 @@ export default function TokenAllowance({
noBorder noBorder
supportsEIP1559={supportsEIP1559} supportsEIP1559={supportsEIP1559}
isSetApproveForAll={isSetApproveForAll} isSetApproveForAll={isSetApproveForAll}
fullTxData={fullTxData}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
renderSimulationFailureWarning={renderSimulationFailureWarning}
isApprovalOrRejection={isApprovalOrRejection} isApprovalOrRejection={isApprovalOrRejection}
data={customTxParamsData || data} data={customTxParamsData || data}
/> />