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

Using EIP-1559 V2 for approve transaction flow (#12906)

This commit is contained in:
Jyoti Puri 2021-12-06 22:09:35 +05:30 committed by GitHub
parent a6bb503e52
commit 6fc9b6b7a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 414 additions and 329 deletions

View File

@ -13,6 +13,7 @@
@import 'connected-status-indicator/index';
@import 'edit-gas-display/index';
@import 'edit-gas-display-education/index';
@import 'edit-gas-fee-button/index';
@import 'edit-gas-fee-popover/index';
@import 'edit-gas-fee-popover/edit-gas-item/index';
@import 'edit-gas-fee-popover/network-statistics/index';

View File

@ -0,0 +1,73 @@
import React from 'react';
import PropTypes from 'prop-types';
import { COLORS } from '../../../helpers/constants/design-system';
import { PRIORITY_LEVEL_ICON_MAP } from '../../../helpers/constants/gas';
import { useGasFeeContext } from '../../../contexts/gasFee';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useTransactionModalContext } from '../../../contexts/transaction-modal';
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
import Typography from '../../ui/typography/typography';
export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
const t = useI18nContext();
const {
gasLimit,
hasSimulationError,
estimateUsed,
maxFeePerGas,
maxPriorityFeePerGas,
supportsEIP1559V2,
transaction,
} = useGasFeeContext();
const { openModal } = useTransactionModalContext();
const editEnabled =
!hasSimulationError || userAcknowledgedGasMissing === true;
if (!supportsEIP1559V2 || !estimateUsed || !editEnabled) {
return null;
}
return (
<div className="edit-gas-fee-button">
<button onClick={() => openModal('editGasFee')}>
<span className="edit-gas-fee-button__icon">
{`${PRIORITY_LEVEL_ICON_MAP[estimateUsed]} `}
</span>
<span className="edit-gas-fee-button__label">{t(estimateUsed)}</span>
<i className="fas fa-chevron-right asset-list-item__chevron-right" />
</button>
{estimateUsed === 'custom' && (
<button onClick={() => openModal('advancedGasFee')}>{t('edit')}</button>
)}
{estimateUsed === 'dappSuggested' && (
<InfoTooltip
contentText={
<div className="edit-gas-fee-button__tooltip">
<Typography fontSize="12px" color={COLORS.GREY}>
{t('dappSuggestedTooltip', [transaction.origin])}
</Typography>
<Typography fontSize="12px">
<b>{t('maxBaseFee')}</b>
{maxFeePerGas}
</Typography>
<Typography fontSize="12px">
<b>{t('maxPriorityFee')}</b>
{maxPriorityFeePerGas}
</Typography>
<Typography fontSize="12px">
<b>{t('gasLimit')}</b>
{gasLimit}
</Typography>
</div>
}
position="top"
/>
)}
</div>
);
}
EditGasFeeButton.propTypes = {
userAcknowledgedGasMissing: PropTypes.bool,
};

View File

@ -0,0 +1,140 @@
import React from 'react';
import { screen } from '@testing-library/react';
import {
GAS_ESTIMATE_TYPES,
PRIORITY_LEVELS,
} from '../../../../shared/constants/gas';
import { TRANSACTION_ENVELOPE_TYPES } from '../../../../shared/constants/transaction';
import { GasFeeContextProvider } from '../../../contexts/gasFee';
import { renderWithProvider } from '../../../../test/jest';
import mockEstimates from '../../../../test/data/mock-estimates.json';
import mockState from '../../../../test/data/mock-state.json';
import configureStore from '../../../store/store';
import EditGasFeeButton from './edit-gas-fee-button';
jest.mock('../../../store/actions', () => ({
disconnectGasFeeEstimatePoller: jest.fn(),
getGasFeeEstimatesAndStartPolling: jest
.fn()
.mockImplementation(() => Promise.resolve()),
addPollingTokenToAppState: jest.fn(),
}));
const render = ({ componentProps, contextProps } = {}) => {
const store = configureStore({
metamask: {
...mockState.metamask,
accounts: {
[mockState.metamask.selectedAddress]: {
address: mockState.metamask.selectedAddress,
balance: '0x1F4',
},
},
gasFeeEstimates: mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET],
},
});
return renderWithProvider(
<GasFeeContextProvider {...contextProps}>
<EditGasFeeButton {...componentProps} />
</GasFeeContextProvider>,
store,
);
};
describe('EditGasFeeButton', () => {
beforeEach(() => {
process.env.EIP_1559_V2 = true;
});
afterEach(() => {
process.env.EIP_1559_V2 = false;
});
it('should render edit link with text low if low gas estimates are selected', () => {
render({ contextProps: { transaction: { userFeeLevel: 'low' } } });
expect(screen.queryByText('🐢')).toBeInTheDocument();
expect(screen.queryByText('Low')).toBeInTheDocument();
});
it('should render edit link with text market if medium gas estimates are selected', () => {
render({ contextProps: { transaction: { userFeeLevel: 'medium' } } });
expect(screen.queryByText('🦊')).toBeInTheDocument();
expect(screen.queryByText('Market')).toBeInTheDocument();
});
it('should render edit link with text agressive if high gas estimates are selected', () => {
render({ contextProps: { transaction: { userFeeLevel: 'high' } } });
expect(screen.queryByText('🦍')).toBeInTheDocument();
expect(screen.queryByText('Aggressive')).toBeInTheDocument();
});
it('should render edit link with text Site suggested if site suggested estimated are used', () => {
render({
contextProps: {
transaction: {
userFeeLevel: PRIORITY_LEVELS.DAPP_SUGGESTED,
dappSuggestedGasFees: { maxFeePerGas: 1, maxPriorityFeePerGas: 1 },
txParams: { maxFeePerGas: 1, maxPriorityFeePerGas: 1 },
},
},
});
expect(screen.queryByText('🌐')).toBeInTheDocument();
expect(screen.queryByText('Site suggested')).toBeInTheDocument();
expect(document.getElementsByClassName('info-tooltip')).toHaveLength(1);
});
it('should render edit link with text advance if custom gas estimates are used', () => {
render({
contextProps: {
defaultEstimateToUse: 'custom',
},
});
expect(screen.queryByText('⚙')).toBeInTheDocument();
expect(screen.queryByText('Advanced')).toBeInTheDocument();
expect(screen.queryByText('Edit')).toBeInTheDocument();
});
it('should not render edit link if transaction has simulation error and prop userAcknowledgedGasMissing is false', () => {
render({
contextProps: {
transaction: {
simulationFails: true,
userFeeLevel: 'low',
},
},
componentProps: { userAcknowledgedGasMissing: false },
});
expect(screen.queryByRole('button')).not.toBeInTheDocument();
expect(screen.queryByText('Low')).not.toBeInTheDocument();
});
it('should render edit link if userAcknowledgedGasMissing is true even if transaction has simulation error', () => {
render({
contextProps: {
transaction: {
simulationFails: true,
userFeeLevel: 'low',
},
},
componentProps: { userAcknowledgedGasMissing: true },
});
expect(screen.queryByRole('button')).toBeInTheDocument();
expect(screen.queryByText('Low')).toBeInTheDocument();
});
it('should render null for legacy transactions', () => {
const { container } = render({
contextProps: {
transaction: {
userFeeLevel: 'low',
txParams: { type: TRANSACTION_ENVELOPE_TYPES.LEGACY },
},
},
});
expect(container.firstChild).toBeNull();
});
});

View File

@ -0,0 +1 @@
export { default } from './edit-gas-fee-button';

View File

@ -0,0 +1,48 @@
.edit-gas-fee-button {
display: flex;
align-items: baseline;
justify-content: flex-end;
button {
@include H7;
display: flex;
align-items: baseline;
color: $primary-1;
background: transparent;
border: 0;
padding-inline-end: 0;
white-space: pre;
}
i {
color: $primary-1;
margin-right: 2px;
}
&__icon {
font-size: 16px;
}
&__label {
font-size: 12px;
margin-right: 8px;
}
.info-tooltip {
align-self: center;
margin-left: 6px;
}
&__tooltip {
p {
color: $Grey-500;
}
b {
color: $neutral-black;
display: inline-block;
min-width: 60%;
}
}
}

View File

@ -44,15 +44,15 @@ const EditGasItem = ({ priorityLevel }) => {
if (gasFeeEstimates?.[priorityLevel]) {
maxFeePerGas = gasFeeEstimates[priorityLevel].suggestedMaxFeePerGas;
maxPriorityFeePerGas =
gasFeeEstimates[priorityLevel].suggestedMaxPriorityFeePerGas;
} else if (
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED &&
dappSuggestedGasFees
) {
maxFeePerGas = hexWEIToDecGWEI(dappSuggestedGasFees.maxFeePerGas);
maxFeePerGas = hexWEIToDecGWEI(
dappSuggestedGasFees.maxFeePerGas || dappSuggestedGasFees.gasPrice,
);
maxPriorityFeePerGas = hexWEIToDecGWEI(
dappSuggestedGasFees.maxPriorityFeePerGas,
dappSuggestedGasFees.maxPriorityFeePerGas || maxFeePerGas,
);
} else if (priorityLevel === PRIORITY_LEVELS.CUSTOM) {
if (estimateUsed === PRIORITY_LEVELS.CUSTOM) {

View File

@ -118,7 +118,10 @@ describe('EditGasItem', () => {
it('should renders site gas estimate option for priorityLevel dappSuggested', () => {
renderComponent(
{ priorityLevel: 'dappSuggested' },
{ dappSuggestedGasFees: DAPP_SUGGESTED_ESTIMATE },
{
dappSuggestedGasFees: DAPP_SUGGESTED_ESTIMATE,
txParams: { gas: '0x5208', ...DAPP_SUGGESTED_ESTIMATE },
},
);
expect(
screen.queryByRole('button', { name: 'dappSuggested' }),

View File

@ -16,72 +16,6 @@
}
}
&-edit-V2 {
display: flex;
align-items: baseline;
justify-content: flex-end;
padding-top: 20px;
button {
@include H7;
display: flex;
align-items: baseline;
color: $primary-1;
background: transparent;
border: 0;
padding-inline-end: 0;
position: relative;
white-space: pre;
}
&__edit-button {
margin-left: 8px;
}
i {
color: $primary-1;
margin-right: 2px;
}
&__icon {
@include Paragraph;
line-height: 1;
position: absolute;
left: -16px;
top: 0;
&-custom {
font-size: 1.35rem;
left: -12px;
top: -2px;
}
}
&__label {
font-size: 12px;
margin-right: 8px;
}
.info-tooltip {
align-self: center;
margin-left: 6px;
}
&__tooltip {
p {
color: $Grey-500;
}
b {
color: $neutral-black;
display: inline-block;
min-width: 60%;
}
}
}
&-rows {
margin-top: 10px;
}

View File

@ -2,14 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useGasFeeContext } from '../../../contexts/gasFee';
import { useTransactionModalContext } from '../../../contexts/transaction-modal';
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
import Typography from '../../ui/typography/typography';
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
import { PRIORITY_LEVEL_ICON_MAP } from '../../../helpers/constants/gas';
import { useI18nContext } from '../../../hooks/useI18nContext';
import Box from '../../ui/box';
import EditGasFeeButton from '../edit-gas-fee-button';
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
export default function TransactionDetail({
rows = [],
@ -17,89 +14,18 @@ export default function TransactionDetail({
userAcknowledgedGasMissing,
}) {
const t = useI18nContext();
const {
gasLimit,
hasSimulationError,
estimateUsed,
maxFeePerGas,
maxPriorityFeePerGas,
supportsEIP1559V2,
transaction,
} = useGasFeeContext();
const { openModal } = useTransactionModalContext();
if (supportsEIP1559V2 && estimateUsed) {
const editEnabled = !hasSimulationError || userAcknowledgedGasMissing;
if (!editEnabled) return null;
return (
<div className="transaction-detail">
<div className="transaction-detail-edit-V2">
<button onClick={() => openModal('editGasFee')}>
<span
className={`transaction-detail-edit-V2__icon transaction-detail-edit-V2__icon-${estimateUsed}`}
>
{`${PRIORITY_LEVEL_ICON_MAP[estimateUsed]} `}
</span>
<span className="transaction-detail-edit-V2__label">
{t(estimateUsed)}
</span>
<i className="fas fa-chevron-right asset-list-item__chevron-right" />
</button>
{estimateUsed === 'custom' && (
<button
onClick={() => openModal('advancedGasFee')}
className="transaction-detail__edit-button"
>
{t('edit')}
</button>
)}
{estimateUsed === 'dappSuggested' && (
<InfoTooltip
contentText={
<div className="transaction-detail-edit-V2__tooltip">
<Typography
tag={TYPOGRAPHY.Paragraph}
variant={TYPOGRAPHY.H7}
color={COLORS.GREY}
>
{t('dappSuggestedTooltip', [transaction.origin])}
</Typography>
<Typography
tag={TYPOGRAPHY.Paragraph}
variant={TYPOGRAPHY.H7}
>
<strong>{t('maxBaseFee')}</strong>
{maxFeePerGas}
</Typography>
<Typography
tag={TYPOGRAPHY.Paragraph}
variant={TYPOGRAPHY.H7}
>
<strong>{t('maxPriorityFee')}</strong>
{maxPriorityFeePerGas}
</Typography>
<Typography
tag={TYPOGRAPHY.Paragraph}
variant={TYPOGRAPHY.H7}
>
<strong>{t('gasLimit')}</strong>
{gasLimit}
</Typography>
</div>
}
position="top"
/>
)}
</div>
<div className="transaction-detail-rows">{rows}</div>
</div>
);
}
const { supportsEIP1559V2 } = useGasFeeContext();
return (
<div className="transaction-detail">
{onEdit && (
{supportsEIP1559V2 && (
<Box display="flex" justifyContent="flex-end" paddingTop={5}>
<EditGasFeeButton
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
</Box>
)}
{!supportsEIP1559V2 && onEdit && (
<div className="transaction-detail-edit">
<button onClick={onEdit}>{t('edit')}</button>
</div>

View File

@ -1,10 +1,7 @@
import React from 'react';
import { screen } from '@testing-library/react';
import {
GAS_ESTIMATE_TYPES,
PRIORITY_LEVELS,
} from '../../../../shared/constants/gas';
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
import { TRANSACTION_ENVELOPE_TYPES } from '../../../../shared/constants/transaction';
import { GasFeeContextProvider } from '../../../contexts/gasFee';
@ -67,72 +64,6 @@ describe('TransactionDetail', () => {
expect(screen.queryByText('Low')).toBeInTheDocument();
});
it('should render edit link with text markey if medium gas estimates are selected', () => {
render({ contextProps: { transaction: { userFeeLevel: 'medium' } } });
expect(screen.queryByText('🦊')).toBeInTheDocument();
expect(screen.queryByText('Market')).toBeInTheDocument();
});
it('should render edit link with text agressive if high gas estimates are selected', () => {
render({ contextProps: { transaction: { userFeeLevel: 'high' } } });
expect(screen.queryByText('🦍')).toBeInTheDocument();
expect(screen.queryByText('Aggressive')).toBeInTheDocument();
});
it('should render edit link with text Site suggested if site suggested estimated are used', () => {
render({
contextProps: {
transaction: {
userFeeLevel: PRIORITY_LEVELS.DAPP_SUGGESTED,
dappSuggestedGasFees: { maxFeePerGas: 1, maxPriorityFeePerGas: 1 },
txParams: { maxFeePerGas: 1, maxPriorityFeePerGas: 1 },
},
},
});
expect(screen.queryByText('🌐')).toBeInTheDocument();
expect(screen.queryByText('Site suggested')).toBeInTheDocument();
expect(document.getElementsByClassName('info-tooltip')).toHaveLength(1);
});
it('should render edit link with text advance if custom gas estimates are used', () => {
render({
contextProps: {
defaultEstimateToUse: 'custom',
},
});
expect(screen.queryByText('⚙')).toBeInTheDocument();
expect(screen.queryByText('Advanced')).toBeInTheDocument();
expect(screen.queryByText('Edit')).toBeInTheDocument();
});
it('should not render edit link if transaction has simulation error and prop userAcknowledgedGasMissing is false', () => {
render({
contextProps: {
transaction: {
simulationFails: true,
userFeeLevel: 'low',
},
},
componentProps: { userAcknowledgedGasMissing: false },
});
expect(screen.queryByRole('button')).not.toBeInTheDocument();
expect(screen.queryByText('Low')).not.toBeInTheDocument();
});
it('should render edit link if userAcknowledgedGasMissing is true even if transaction has simulation error', () => {
render({
contextProps: {
transaction: {
simulationFails: true,
userFeeLevel: 'low',
},
},
componentProps: { userAcknowledgedGasMissing: true },
});
expect(screen.queryByRole('button')).toBeInTheDocument();
expect(screen.queryByText('Low')).toBeInTheDocument();
});
it('should render edit link with text edit for legacy transactions', () => {
render({
contextProps: {

View File

@ -12,6 +12,7 @@ export const TransactionModalContextProvider = ({
actionKey,
children,
methodData,
captureEventEnabled = true,
}) => {
const [openModals, setOpenModals] = useState([]);
const metricsEvent = useMetaMetricsContext();
@ -28,7 +29,7 @@ export const TransactionModalContextProvider = ({
recipientKnown: null,
functionType:
actionKey ||
getMethodName(methodData.name) ||
getMethodName(methodData?.name) ||
TRANSACTION_TYPES.CONTRACT_INTERACTION,
origin,
},
@ -49,7 +50,7 @@ export const TransactionModalContextProvider = ({
const openModal = (modalName) => {
if (openModals.includes(modalName)) return;
captureEvent();
captureEventEnabled && captureEvent();
const modals = [...openModals];
modals.push(modalName);
setOpenModals(modals);
@ -77,4 +78,5 @@ TransactionModalContextProvider.propTypes = {
actionKey: PropTypes.string,
children: PropTypes.node.isRequired,
methodData: PropTypes.object,
captureEventEnabled: PropTypes.bool,
};

View File

@ -11,6 +11,7 @@ import { ellipsify } from '../../send/send.utils';
import Typography from '../../../components/ui/typography';
import Box from '../../../components/ui/box';
import Button from '../../../components/ui/button';
import EditGasFeeButton from '../../../components/app/edit-gas-fee-button';
import MetaFoxLogo from '../../../components/ui/metafox-logo';
import Identicon from '../../../components/ui/identicon';
import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message';
@ -64,6 +65,7 @@ export default class ConfirmApproveContent extends Component {
isContract: PropTypes.bool,
hexTransactionTotal: PropTypes.string,
isMultiLayerFeeNetwork: PropTypes.bool,
supportsEIP1559V2: PropTypes.bool,
};
state = {
@ -76,11 +78,13 @@ export default class ConfirmApproveContent extends Component {
symbol,
title,
showEdit,
showAdvanceGasFeeOptions = false,
onEditClick,
content,
footer,
noBorder,
}) {
const { supportsEIP1559V2 } = this.props;
const { t } = this.context;
return (
<div
@ -97,7 +101,7 @@ export default class ConfirmApproveContent extends Component {
<div className="confirm-approve-content__card-header__title">
{title}
</div>
{showEdit && (
{showEdit && (!showAdvanceGasFeeOptions || !supportsEIP1559V2) && (
<Box width={BLOCK_SIZES.ONE_SIXTH}>
<Button
type="link"
@ -108,6 +112,9 @@ export default class ConfirmApproveContent extends Component {
</Button>
</Box>
)}
{showEdit && showAdvanceGasFeeOptions && supportsEIP1559V2 && (
<EditGasFeeButton />
)}
</div>
)}
<div className="confirm-approve-content__card-content">{content}</div>
@ -445,6 +452,7 @@ export default class ConfirmApproveContent extends Component {
symbol: <i className="fa fa-tag" />,
title: t('transactionFee'),
showEdit: true,
showAdvanceGasFeeOptions: true,
onEditClick: showCustomizeGasModal,
content: this.renderTransactionDetailsContent(),
noBorder: useNonceField || !showFullTxDetails,

View File

@ -15,6 +15,8 @@ import {
getTokenValueParam,
} from '../../helpers/utils/token-util';
import { readAddressAsContract } from '../../../shared/modules/contract-utils';
import { GasFeeContextProvider } from '../../contexts/gasFee';
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
import { useTokenTracker } from '../../hooks/useTokenTracker';
import {
getTokens,
@ -32,13 +34,14 @@ import {
getCurrentChainId,
getRpcPrefsForCurrentProvider,
getIsMultiLayerFeeNetwork,
checkNetworkAndAccountSupports1559,
} from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { currentNetworkTxListSelector } from '../../selectors/transactions';
import Loading from '../../components/ui/loading-screen';
import AdvancedGasFeePopover from '../../components/app/advanced-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 Loading from '../../components/ui/loading-screen';
import { isEqualCaseInsensitive } from '../../helpers/utils/util';
import { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content';
@ -47,6 +50,10 @@ const isAddressLedgerByFromAddress = (address) => (state) => {
return isAddressLedger(state, address);
};
// eslint-disable-next-line prefer-destructuring
const EIP_1559_V2_ENABLED =
process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true';
export default function ConfirmApprove() {
const dispatch = useDispatch();
const { id: paramsTransactionId } = useParams();
@ -66,6 +73,9 @@ export default function ConfirmApprove() {
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const isMultiLayerFeeNetwork = useSelector(getIsMultiLayerFeeNetwork);
const networkAndAccountSupports1559 = useSelector(
checkNetworkAndAccountSupports1559,
);
const fromAddressIsLedger = useSelector(isAddressLedgerByFromAddress(from));
@ -79,6 +89,9 @@ export default function ConfirmApprove() {
hexTransactionTotal,
} = useSelector((state) => transactionFeeSelector(state, transaction));
const supportsEIP1559V2 =
EIP_1559_V2_ENABLED && networkAndAccountSupports1559;
const currentToken = (tokens &&
tokens.find(({ address }) =>
isEqualCaseInsensitive(tokenAddress, address),
@ -163,101 +176,106 @@ export default function ConfirmApprove() {
return tokenSymbol === undefined ? (
<Loading />
) : (
<ConfirmTransactionBase
toAddress={toAddress}
identiconAddress={tokenAddress}
showAccountInHeader
title={tokensText}
contentComponent={
<>
<ConfirmApproveContent
decimals={decimals}
siteImage={siteImage}
setCustomAmount={setCustomPermissionAmount}
customTokenAmount={String(customPermissionAmount)}
tokenAmount={tokenAmount}
origin={formattedOrigin}
tokenSymbol={tokenSymbol}
tokenImage={tokenImage}
tokenBalance={tokenBalance}
showCustomizeGasModal={approveTransaction}
showEditApprovalPermissionModal={({
/* eslint-disable no-shadow */
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
/* eslint-enable no-shadow */
}) =>
dispatch(
showModal({
name: 'EDIT_APPROVAL_PERMISSION',
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
}),
)
}
data={customData || data}
toAddress={toAddress}
currentCurrency={currentCurrency}
nativeCurrency={nativeCurrency}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
useNonceField={useNonceField}
nextNonce={nextNonce}
customNonceValue={customNonceValue}
updateCustomNonce={(value) => {
dispatch(updateCustomNonce(value));
}}
getNextNonce={() => dispatch(getNextNonce())}
showCustomizeNonceModal={({
/* eslint-disable no-shadow */
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
/* eslint-disable no-shadow */
}) =>
dispatch(
showModal({
name: 'CUSTOMIZE_NONCE',
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
}),
)
}
warning={submitWarning}
txData={transaction}
fromAddressIsLedger={fromAddressIsLedger}
chainId={chainId}
rpcPrefs={rpcPrefs}
isContract={isContract}
isMultiLayerFeeNetwork={isMultiLayerFeeNetwork}
/>
{showCustomizeGasPopover && (
<EditGasPopover
onClose={closeCustomizeGasPopover}
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
transaction={transaction}
<GasFeeContextProvider transaction={transaction}>
<ConfirmTransactionBase
toAddress={toAddress}
identiconAddress={tokenAddress}
showAccountInHeader
title={tokensText}
contentComponent={
<TransactionModalContextProvider captureEventEnabled={false}>
<ConfirmApproveContent
decimals={decimals}
siteImage={siteImage}
setCustomAmount={setCustomPermissionAmount}
customTokenAmount={String(customPermissionAmount)}
tokenAmount={tokenAmount}
origin={formattedOrigin}
tokenSymbol={tokenSymbol}
tokenImage={tokenImage}
tokenBalance={tokenBalance}
showCustomizeGasModal={approveTransaction}
showEditApprovalPermissionModal={({
/* eslint-disable no-shadow */
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
/* eslint-enable no-shadow */
}) =>
dispatch(
showModal({
name: 'EDIT_APPROVAL_PERMISSION',
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
}),
)
}
data={customData || data}
toAddress={toAddress}
currentCurrency={currentCurrency}
nativeCurrency={nativeCurrency}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
useNonceField={useNonceField}
nextNonce={nextNonce}
customNonceValue={customNonceValue}
updateCustomNonce={(value) => {
dispatch(updateCustomNonce(value));
}}
getNextNonce={() => dispatch(getNextNonce())}
showCustomizeNonceModal={({
/* eslint-disable no-shadow */
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
/* eslint-disable no-shadow */
}) =>
dispatch(
showModal({
name: 'CUSTOMIZE_NONCE',
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
}),
)
}
warning={submitWarning}
txData={transaction}
fromAddressIsLedger={fromAddressIsLedger}
chainId={chainId}
rpcPrefs={rpcPrefs}
isContract={isContract}
isMultiLayerFeeNetwork={isMultiLayerFeeNetwork}
supportsEIP1559V2={supportsEIP1559V2}
/>
)}
</>
}
hideSenderToRecipient
customTxParamsData={customData}
/>
{showCustomizeGasPopover && !supportsEIP1559V2 && (
<EditGasPopover
onClose={closeCustomizeGasPopover}
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
transaction={transaction}
/>
)}
<EditGasFeePopover />
<AdvancedGasFeePopover />
</TransactionModalContextProvider>
}
hideSenderToRecipient
customTxParamsData={customData}
/>
</GasFeeContextProvider>
);
}