mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Using EIP-1559 V2 for swaps (#12966)
This commit is contained in:
parent
2085352de8
commit
19c3d021ea
@ -2899,6 +2899,12 @@
|
||||
"swapSourceInfo": {
|
||||
"message": "We search multiple liquidity sources (exchanges, aggregators and professional market makers) to find the best rates and lowest network fees."
|
||||
},
|
||||
"swapSuggested": {
|
||||
"message": "Swap suggested"
|
||||
},
|
||||
"swapSuggestedGasSettingToolTipMessage": {
|
||||
"message": "Swaps are complex and time sensitive transactions. We recommend this gas fee for a good balance between cost and confidence of a successful Swap."
|
||||
},
|
||||
"swapSwapFrom": {
|
||||
"message": "Swap from"
|
||||
},
|
||||
|
@ -258,8 +258,12 @@ export default class ConfirmPageContainer extends Component {
|
||||
transaction={currentTransaction}
|
||||
/>
|
||||
)}
|
||||
<EditGasFeePopover />
|
||||
<AdvancedGasFeePopover />
|
||||
{supportsEIP1559V2 && (
|
||||
<>
|
||||
<EditGasFeePopover />
|
||||
<AdvancedGasFeePopover />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</GasFeeContextProvider>
|
||||
);
|
||||
|
@ -1,7 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { COLORS } from '../../../helpers/constants/design-system';
|
||||
import {
|
||||
EDIT_GAS_MODES,
|
||||
PRIORITY_LEVELS,
|
||||
} from '../../../../shared/constants/gas';
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||
import { PRIORITY_LEVEL_ICON_MAP } from '../../../helpers/constants/gas';
|
||||
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
@ -12,6 +16,7 @@ import Typography from '../../ui/typography/typography';
|
||||
export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
|
||||
const t = useI18nContext();
|
||||
const {
|
||||
editGasMode,
|
||||
gasLimit,
|
||||
hasSimulationError,
|
||||
estimateUsed,
|
||||
@ -28,13 +33,23 @@ export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let icon = estimateUsed;
|
||||
let title = estimateUsed;
|
||||
if (
|
||||
estimateUsed === PRIORITY_LEVELS.HIGH &&
|
||||
editGasMode === EDIT_GAS_MODES.SWAPS
|
||||
) {
|
||||
icon = 'swapSuggested';
|
||||
title = 'swapSuggested';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="edit-gas-fee-button">
|
||||
<button onClick={() => openModal('editGasFee')}>
|
||||
<span className="edit-gas-fee-button__icon">
|
||||
{`${PRIORITY_LEVEL_ICON_MAP[estimateUsed]} `}
|
||||
{`${PRIORITY_LEVEL_ICON_MAP[icon]} `}
|
||||
</span>
|
||||
<span className="edit-gas-fee-button__label">{t(estimateUsed)}</span>
|
||||
<span className="edit-gas-fee-button__label">{t(title)}</span>
|
||||
<i className="fas fa-chevron-right asset-list-item__chevron-right" />
|
||||
</button>
|
||||
{estimateUsed === 'custom' && (
|
||||
@ -44,18 +59,18 @@ export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
|
||||
<InfoTooltip
|
||||
contentText={
|
||||
<div className="edit-gas-fee-button__tooltip">
|
||||
<Typography fontSize="12px" color={COLORS.GREY}>
|
||||
<Typography variant={TYPOGRAPHY.H7} color={COLORS.GREY}>
|
||||
{t('dappSuggestedTooltip', [transaction.origin])}
|
||||
</Typography>
|
||||
<Typography fontSize="12px">
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
<b>{t('maxBaseFee')}</b>
|
||||
{maxFeePerGas}
|
||||
</Typography>
|
||||
<Typography fontSize="12px">
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
<b>{t('maxPriorityFee')}</b>
|
||||
{maxPriorityFeePerGas}
|
||||
</Typography>
|
||||
<Typography fontSize="12px">
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
<b>{t('gasLimit')}</b>
|
||||
{gasLimit}
|
||||
</Typography>
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import {
|
||||
EDIT_GAS_MODES,
|
||||
GAS_ESTIMATE_TYPES,
|
||||
PRIORITY_LEVELS,
|
||||
} from '../../../../shared/constants/gas';
|
||||
@ -87,6 +88,17 @@ describe('EditGasFeeButton', () => {
|
||||
expect(document.getElementsByClassName('info-tooltip')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render edit link with text swap suggested if high gas estimates are selected for swaps', () => {
|
||||
render({
|
||||
contextProps: {
|
||||
transaction: { userFeeLevel: 'high' },
|
||||
editGasMode: EDIT_GAS_MODES.SWAPS,
|
||||
},
|
||||
});
|
||||
expect(screen.queryByText('🔄')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Swap suggested')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render edit link with text advance if custom gas estimates are used', () => {
|
||||
render({
|
||||
contextProps: {
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PRIORITY_LEVELS } from '../../../../shared/constants/gas';
|
||||
import {
|
||||
EDIT_GAS_MODES,
|
||||
PRIORITY_LEVELS,
|
||||
} from '../../../../shared/constants/gas';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { useTransactionModalContext } from '../../../contexts/transaction-modal';
|
||||
import ErrorMessage from '../../ui/error-message';
|
||||
@ -15,7 +18,7 @@ import EditGasItem from './edit-gas-item';
|
||||
import NetworkStatistics from './network-statistics';
|
||||
|
||||
const EditGasFeePopover = () => {
|
||||
const { balanceError } = useGasFeeContext();
|
||||
const { balanceError, editGasMode } = useGasFeeContext();
|
||||
const t = useI18nContext();
|
||||
const { closeModal, currentModal } = useTransactionModalContext();
|
||||
|
||||
@ -38,17 +41,23 @@ const EditGasFeePopover = () => {
|
||||
<I18nValue messageKey="gasOption" />
|
||||
</span>
|
||||
<span className="edit-gas-fee-popover__content__header-time">
|
||||
<I18nValue messageKey="time" />
|
||||
{editGasMode !== EDIT_GAS_MODES.SWAPS && (
|
||||
<I18nValue messageKey="time" />
|
||||
)}
|
||||
</span>
|
||||
<span className="edit-gas-fee-popover__content__header-max-fee">
|
||||
<I18nValue messageKey="maxFee" />
|
||||
</span>
|
||||
</div>
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.LOW} />
|
||||
{editGasMode !== EDIT_GAS_MODES.SWAPS && (
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.LOW} />
|
||||
)}
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.MEDIUM} />
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.HIGH} />
|
||||
<div className="edit-gas-fee-popover__content__separator" />
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.DAPP_SUGGESTED} />
|
||||
{editGasMode !== EDIT_GAS_MODES.SWAPS && (
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.DAPP_SUGGESTED} />
|
||||
)}
|
||||
<EditGasItem priorityLevel={PRIORITY_LEVELS.CUSTOM} />
|
||||
<NetworkStatistics />
|
||||
<Typography
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import { ETH } from '../../../helpers/constants/common';
|
||||
import configureStore from '../../../store/store';
|
||||
@ -45,7 +46,7 @@ const MOCK_FEE_ESTIMATE = {
|
||||
estimatedBaseFee: '50',
|
||||
};
|
||||
|
||||
const render = (txProps) => {
|
||||
const render = ({ txProps, contextProps } = {}) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
@ -66,6 +67,7 @@ const render = (txProps) => {
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider
|
||||
transaction={{ txParams: { gas: '0x5208' }, ...txProps }}
|
||||
{...contextProps}
|
||||
>
|
||||
<EditGasFeePopover />
|
||||
</GasFeeContextProvider>,
|
||||
@ -75,7 +77,7 @@ const render = (txProps) => {
|
||||
|
||||
describe('EditGasFeePopover', () => {
|
||||
it('should renders low / medium / high options', () => {
|
||||
render({ dappSuggestedGasFees: {} });
|
||||
render({ txProps: { dappSuggestedGasFees: {} } });
|
||||
|
||||
expect(screen.queryByText('🐢')).toBeInTheDocument();
|
||||
expect(screen.queryByText('🦊')).toBeInTheDocument();
|
||||
@ -103,12 +105,40 @@ describe('EditGasFeePopover', () => {
|
||||
});
|
||||
|
||||
it('should not show insufficient balance message if transaction value is less than balance', () => {
|
||||
render({ userFeeLevel: 'high', txParams: { value: '0x64' } });
|
||||
render({ txProps: { userFeeLevel: 'high', txParams: { value: '0x64' } } });
|
||||
expect(screen.queryByText('Insufficient funds.')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show insufficient balance message if transaction value is more than balance', () => {
|
||||
render({ userFeeLevel: 'high', txParams: { value: '0x5208' } });
|
||||
render({
|
||||
txProps: { userFeeLevel: 'high', txParams: { value: '0x5208' } },
|
||||
});
|
||||
expect(screen.queryByText('Insufficient funds.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show low, aggressive and dapp-suggested options for swap', () => {
|
||||
render({
|
||||
contextProps: { editGasMode: EDIT_GAS_MODES.SWAPS },
|
||||
});
|
||||
expect(screen.queryByText('🐢')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('🦊')).toBeInTheDocument();
|
||||
expect(screen.queryByText('🦍')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('🌐')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('🔄')).toBeInTheDocument();
|
||||
expect(screen.queryByText('⚙')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Low')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Market')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Aggressive')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Site')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Swap suggested')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Advanced')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show time estimates for swaps', () => {
|
||||
render({
|
||||
contextProps: { editGasMode: EDIT_GAS_MODES.SWAPS },
|
||||
});
|
||||
expect(screen.queryByText('Time')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Max fee')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -4,7 +4,10 @@ import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getMaximumGasTotalInHexWei } from '../../../../../shared/modules/gas.utils';
|
||||
import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas';
|
||||
import {
|
||||
EDIT_GAS_MODES,
|
||||
PRIORITY_LEVELS,
|
||||
} from '../../../../../shared/constants/gas';
|
||||
import { PRIORITY_LEVEL_ICON_MAP } from '../../../../helpers/constants/gas';
|
||||
import { PRIMARY } from '../../../../helpers/constants/common';
|
||||
import {
|
||||
@ -27,17 +30,19 @@ import { useCustomTimeEstimate } from './useCustomTimeEstimate';
|
||||
|
||||
const EditGasItem = ({ priorityLevel }) => {
|
||||
const {
|
||||
editGasMode,
|
||||
estimateUsed,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
maxFeePerGas: maxFeePerGasValue,
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGasValue,
|
||||
updateTransactionUsingGasFeeEstimates,
|
||||
transaction: { dappSuggestedGasFees },
|
||||
transaction,
|
||||
} = useGasFeeContext();
|
||||
const t = useI18nContext();
|
||||
const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues);
|
||||
const { closeModal, openModal } = useTransactionModalContext();
|
||||
const { dappSuggestedGasFees } = transaction;
|
||||
|
||||
let maxFeePerGas;
|
||||
let maxPriorityFeePerGas;
|
||||
@ -45,6 +50,8 @@ const EditGasItem = ({ priorityLevel }) => {
|
||||
|
||||
if (gasFeeEstimates?.[priorityLevel]) {
|
||||
maxFeePerGas = gasFeeEstimates[priorityLevel].suggestedMaxFeePerGas;
|
||||
maxPriorityFeePerGas =
|
||||
gasFeeEstimates[priorityLevel].suggestedMaxPriorityFeePerGas;
|
||||
} else if (
|
||||
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED &&
|
||||
dappSuggestedGasFees
|
||||
@ -105,6 +112,18 @@ const EditGasItem = ({ priorityLevel }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
let icon = priorityLevel;
|
||||
let title = priorityLevel;
|
||||
if (priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED) {
|
||||
title = 'dappSuggestedShortLabel';
|
||||
} else if (
|
||||
priorityLevel === PRIORITY_LEVELS.HIGH &&
|
||||
editGasMode === EDIT_GAS_MODES.SWAPS
|
||||
) {
|
||||
icon = 'swapSuggested';
|
||||
title = 'swapSuggested';
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames('edit-gas-item', {
|
||||
@ -118,22 +137,15 @@ const EditGasItem = ({ priorityLevel }) => {
|
||||
<span
|
||||
className={`edit-gas-item__icon edit-gas-item__icon-${priorityLevel}`}
|
||||
>
|
||||
{PRIORITY_LEVEL_ICON_MAP[priorityLevel]}
|
||||
{PRIORITY_LEVEL_ICON_MAP[icon]}
|
||||
</span>
|
||||
<I18nValue
|
||||
messageKey={
|
||||
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED
|
||||
? 'dappSuggestedShortLabel'
|
||||
: priorityLevel
|
||||
}
|
||||
/>
|
||||
<I18nValue messageKey={title} />
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__time-estimate edit-gas-item__time-estimate-${priorityLevel}`}
|
||||
>
|
||||
{minWaitTime
|
||||
? minWaitTime && toHumanReadableTime(t, minWaitTime)
|
||||
: '--'}
|
||||
{editGasMode !== EDIT_GAS_MODES.SWAPS &&
|
||||
(minWaitTime ? toHumanReadableTime(t, minWaitTime) : '--')}
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__fee-estimate edit-gas-item__fee-estimate-${priorityLevel}`}
|
||||
@ -159,6 +171,9 @@ const EditGasItem = ({ priorityLevel }) => {
|
||||
priorityLevel={priorityLevel}
|
||||
maxFeePerGas={maxFeePerGas}
|
||||
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
||||
editGasMode={editGasMode}
|
||||
gasLimit={gasLimit}
|
||||
transaction={transaction}
|
||||
/>
|
||||
}
|
||||
position="top"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { EDIT_GAS_MODES } from '../../../../../shared/constants/gas';
|
||||
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
|
||||
import { ETH } from '../../../../helpers/constants/common';
|
||||
import configureStore from '../../../../store/store';
|
||||
@ -46,7 +47,11 @@ const DAPP_SUGGESTED_ESTIMATE = {
|
||||
maxPriorityFeePerGas: '0x59682f00',
|
||||
};
|
||||
|
||||
const renderComponent = (componentProps, transactionProps) => {
|
||||
const renderComponent = ({
|
||||
componentProps,
|
||||
transactionProps,
|
||||
contextProps,
|
||||
} = {}) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
@ -71,6 +76,7 @@ const renderComponent = (componentProps, transactionProps) => {
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider
|
||||
transaction={{ txParams: { gas: '0x5208' }, ...transactionProps }}
|
||||
{...contextProps}
|
||||
>
|
||||
<EditGasItem priorityLevel="low" {...componentProps} />
|
||||
</GasFeeContextProvider>,
|
||||
@ -80,7 +86,7 @@ const renderComponent = (componentProps, transactionProps) => {
|
||||
|
||||
describe('EditGasItem', () => {
|
||||
it('should renders low gas estimate option for priorityLevel low', () => {
|
||||
renderComponent({ priorityLevel: 'low' });
|
||||
renderComponent({ componentProps: { priorityLevel: 'low' } });
|
||||
expect(screen.queryByRole('button', { name: 'low' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('🐢')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Low')).toBeInTheDocument();
|
||||
@ -89,7 +95,7 @@ describe('EditGasItem', () => {
|
||||
});
|
||||
|
||||
it('should renders market gas estimate option for priorityLevel medium', () => {
|
||||
renderComponent({ priorityLevel: 'medium' });
|
||||
renderComponent({ componentProps: { priorityLevel: 'medium' } });
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'medium' }),
|
||||
).toBeInTheDocument();
|
||||
@ -100,7 +106,7 @@ describe('EditGasItem', () => {
|
||||
});
|
||||
|
||||
it('should renders aggressive gas estimate option for priorityLevel high', () => {
|
||||
renderComponent({ priorityLevel: 'high' });
|
||||
renderComponent({ componentProps: { priorityLevel: 'high' } });
|
||||
expect(screen.queryByRole('button', { name: 'high' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('🦍')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Aggressive')).toBeInTheDocument();
|
||||
@ -108,21 +114,33 @@ describe('EditGasItem', () => {
|
||||
expect(screen.queryByTitle('0.0021 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render priorityLevel high as "Swap suggested" for swaps', () => {
|
||||
renderComponent({
|
||||
componentProps: { priorityLevel: 'high' },
|
||||
contextProps: { editGasMode: EDIT_GAS_MODES.SWAPS },
|
||||
});
|
||||
expect(screen.queryByRole('button', { name: 'high' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('🔄')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Swap suggested')).toBeInTheDocument();
|
||||
expect(screen.queryByText('15 sec')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle('0.0021 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should highlight option is priorityLevel is currently selected', () => {
|
||||
renderComponent({ priorityLevel: 'high' }, { userFeeLevel: 'high' });
|
||||
renderComponent({
|
||||
componentProps: { priorityLevel: 'high' },
|
||||
transactionProps: { userFeeLevel: 'high' },
|
||||
});
|
||||
expect(
|
||||
document.getElementsByClassName('edit-gas-item--selected'),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should renders site gas estimate option for priorityLevel dappSuggested', () => {
|
||||
renderComponent(
|
||||
{ priorityLevel: 'dappSuggested' },
|
||||
{
|
||||
dappSuggestedGasFees: DAPP_SUGGESTED_ESTIMATE,
|
||||
txParams: { gas: '0x5208', ...DAPP_SUGGESTED_ESTIMATE },
|
||||
},
|
||||
);
|
||||
renderComponent({
|
||||
componentProps: { priorityLevel: 'dappSuggested' },
|
||||
transactionProps: { dappSuggestedGasFees: DAPP_SUGGESTED_ESTIMATE },
|
||||
});
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'dappSuggested' }),
|
||||
).toBeInTheDocument();
|
||||
@ -132,7 +150,10 @@ describe('EditGasItem', () => {
|
||||
});
|
||||
|
||||
it('should renders advance gas estimate option for priorityLevel custom', () => {
|
||||
renderComponent({ priorityLevel: 'custom' }, { userFeeLevel: 'high' });
|
||||
renderComponent({
|
||||
componentProps: { priorityLevel: 'custom' },
|
||||
transactionProps: { userFeeLevel: 'high' },
|
||||
});
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'custom' }),
|
||||
).toBeInTheDocument();
|
||||
|
@ -25,6 +25,7 @@
|
||||
color: $ui-black;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
width: 36%;
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,28 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas';
|
||||
import {
|
||||
EDIT_GAS_MODES,
|
||||
PRIORITY_LEVELS,
|
||||
} from '../../../../../shared/constants/gas';
|
||||
import {
|
||||
COLORS,
|
||||
FONT_WEIGHT,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import Typography from '../../../ui/typography';
|
||||
import { useGasFeeContext } from '../../../../contexts/gasFee';
|
||||
|
||||
const EditGasToolTip = ({
|
||||
gasLimit,
|
||||
priorityLevel,
|
||||
// maxFeePerGas & maxPriorityFeePerGas are derived from conditional logic
|
||||
// related to the source of the estimates. We pass these values from the
|
||||
// the parent component (edit-gas-item) rather than recalculate them
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
editGasMode,
|
||||
transaction,
|
||||
t,
|
||||
}) => {
|
||||
const {
|
||||
gasLimit,
|
||||
maxFeePerGas: maxFeePerGasValue,
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGasValue,
|
||||
transaction,
|
||||
} = useGasFeeContext();
|
||||
|
||||
const toolTipMessage = () => {
|
||||
switch (priorityLevel) {
|
||||
case PRIORITY_LEVELS.LOW:
|
||||
@ -40,6 +38,9 @@ const EditGasToolTip = ({
|
||||
</span>,
|
||||
]);
|
||||
case PRIORITY_LEVELS.HIGH:
|
||||
if (editGasMode === EDIT_GAS_MODES.SWAPS) {
|
||||
return t('swapSuggestedGasSettingToolTipMessage');
|
||||
}
|
||||
return t('highGasSettingToolTipMessage', [
|
||||
<span key={priorityLevel}>
|
||||
<b>{t('high')}</b>
|
||||
@ -64,10 +65,15 @@ const EditGasToolTip = ({
|
||||
return (
|
||||
<div className="edit-gas-tooltip__container">
|
||||
{priorityLevel !== PRIORITY_LEVELS.CUSTOM &&
|
||||
priorityLevel !== PRIORITY_LEVELS.DAPP_SUGGESTED ? (
|
||||
priorityLevel !== PRIORITY_LEVELS.DAPP_SUGGESTED &&
|
||||
!(
|
||||
priorityLevel === PRIORITY_LEVELS.HIGH &&
|
||||
editGasMode === EDIT_GAS_MODES.SWAPS
|
||||
) ? (
|
||||
<img alt="" src={`./images/curve-${priorityLevel}.svg`} />
|
||||
) : null}
|
||||
{priorityLevel === PRIORITY_LEVELS.HIGH ? (
|
||||
{priorityLevel === PRIORITY_LEVELS.HIGH &&
|
||||
editGasMode !== EDIT_GAS_MODES.SWAPS ? (
|
||||
<div className="edit-gas-tooltip__container__dialog">
|
||||
<Typography variant={TYPOGRAPHY.H7} color={COLORS.WHITE}>
|
||||
{t('highGasSettingToolTipDialog')}
|
||||
@ -92,7 +98,7 @@ const EditGasToolTip = ({
|
||||
color={COLORS.NEUTRAL_GREY}
|
||||
className="edit-gas-tooltip__container__value"
|
||||
>
|
||||
{maxFeePerGas ?? maxFeePerGasValue}
|
||||
{maxFeePerGas}
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
@ -108,7 +114,7 @@ const EditGasToolTip = ({
|
||||
color={COLORS.NEUTRAL_GREY}
|
||||
className="edit-gas-tooltip__container__value"
|
||||
>
|
||||
{maxPriorityFeePerGas ?? maxPriorityFeePerGasValue}
|
||||
{maxPriorityFeePerGas}
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
@ -138,6 +144,9 @@ EditGasToolTip.propTypes = {
|
||||
maxFeePerGas: PropTypes.string,
|
||||
maxPriorityFeePerGas: PropTypes.string,
|
||||
t: PropTypes.func,
|
||||
editGasMode: PropTypes.string,
|
||||
gasLimit: PropTypes.number,
|
||||
transaction: PropTypes.object,
|
||||
};
|
||||
|
||||
export default EditGasToolTip;
|
||||
|
@ -30,7 +30,7 @@ const HIGH_GAS_OPTION = {
|
||||
maxPriorityFeePerGas: '2',
|
||||
};
|
||||
|
||||
const renderComponent = (props, transactionProps, gasFeeContextProps) => {
|
||||
const renderComponent = (componentProps) => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
provider: {},
|
||||
@ -43,21 +43,14 @@ const renderComponent = (props, transactionProps, gasFeeContextProps) => {
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
featureFlags: { advancedInlineGas: true },
|
||||
advancedGasFee: {
|
||||
maxBaseFee: '1.5',
|
||||
priorityFee: '2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureStore(mockStore);
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider
|
||||
transaction={{ txParams: { gas: '0x5208' }, ...transactionProps }}
|
||||
{...gasFeeContextProps}
|
||||
>
|
||||
<EditGasToolTip {...props} t={jest.fn()} />
|
||||
<GasFeeContextProvider transaction={{ txParams: { gas: '0x5208' } }}>
|
||||
<EditGasToolTip {...componentProps} t={jest.fn()} gasLimit={21000} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ import TransactionDetailItem from '../transaction-detail-item/transaction-detail
|
||||
export default function TransactionDetail({
|
||||
rows = [],
|
||||
onEdit,
|
||||
userAcknowledgedGasMissing,
|
||||
userAcknowledgedGasMissing = false,
|
||||
}) {
|
||||
const t = useI18nContext();
|
||||
const { supportsEIP1559V2 } = useGasFeeContext();
|
||||
@ -44,9 +44,5 @@ TransactionDetail.propTypes = {
|
||||
* onClick handler for the Edit link
|
||||
*/
|
||||
onEdit: PropTypes.func,
|
||||
/**
|
||||
* If there is a error in getting correct estimates we show a message to the user
|
||||
* which they can acknowledge and proceed with their transaction
|
||||
*/
|
||||
userAcknowledgedGasMissing: PropTypes.bool.isRequired,
|
||||
userAcknowledgedGasMissing: PropTypes.bool,
|
||||
};
|
||||
|
@ -37,5 +37,6 @@ export const PRIORITY_LEVEL_ICON_MAP = {
|
||||
medium: '🦊',
|
||||
high: '🦍',
|
||||
dappSuggested: '🌐',
|
||||
swapSuggested: '🔄',
|
||||
custom: '⚙',
|
||||
};
|
||||
|
@ -20,6 +20,10 @@ import {
|
||||
import { ETH } from '../../helpers/constants/common';
|
||||
|
||||
import { useGasFeeEstimates } from '../useGasFeeEstimates';
|
||||
import {
|
||||
getCustomMaxFeePerGas,
|
||||
getCustomMaxPriorityFeePerGas,
|
||||
} from '../../ducks/swaps/swaps';
|
||||
|
||||
// Why this number?
|
||||
// 20 gwei * 21000 gasLimit = 420,000 gwei
|
||||
@ -122,6 +126,12 @@ export const generateUseSelectorRouter = ({
|
||||
balance: '0x440aa47cc2556',
|
||||
};
|
||||
}
|
||||
if (selector === getCustomMaxFeePerGas) {
|
||||
return '0x5208';
|
||||
}
|
||||
if (selector === getCustomMaxPriorityFeePerGas) {
|
||||
return '0x5208';
|
||||
}
|
||||
if (selector === checkNetworkAndAccountSupports1559) {
|
||||
return checkNetworkAndAccountSupports1559Response;
|
||||
}
|
||||
|
@ -170,12 +170,12 @@ export function useGasFeeInputs(
|
||||
maxFeePerGasFiat,
|
||||
setMaxFeePerGas,
|
||||
} = useMaxFeePerGasInput({
|
||||
supportsEIP1559V2,
|
||||
estimateToUse,
|
||||
gasEstimateType,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
gasPrice,
|
||||
supportsEIP1559V2,
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -184,11 +184,11 @@ export function useGasFeeInputs(
|
||||
maxPriorityFeePerGasFiat,
|
||||
setMaxPriorityFeePerGas,
|
||||
} = useMaxPriorityFeePerGasInput({
|
||||
supportsEIP1559V2,
|
||||
estimateToUse,
|
||||
gasEstimateType,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
supportsEIP1559V2,
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -248,6 +248,7 @@ export function useGasFeeInputs(
|
||||
updateTransactionUsingGasFeeEstimates,
|
||||
} = useTransactionFunctions({
|
||||
defaultEstimateToUse,
|
||||
editGasMode,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
transaction,
|
||||
@ -313,6 +314,7 @@ export function useGasFeeInputs(
|
||||
setGasPrice,
|
||||
gasLimit,
|
||||
setGasLimit,
|
||||
editGasMode,
|
||||
estimateToUse,
|
||||
setEstimateToUse,
|
||||
estimatedMinimumFiat,
|
||||
|
@ -4,6 +4,7 @@ import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transactio
|
||||
import {
|
||||
GAS_RECOMMENDATIONS,
|
||||
CUSTOM_GAS_ESTIMATE,
|
||||
EDIT_GAS_MODES,
|
||||
} from '../../../shared/constants/gas';
|
||||
|
||||
import { ETH, PRIMARY } from '../../helpers/constants/common';
|
||||
@ -350,4 +351,13 @@ describe('useGasFeeInputs', () => {
|
||||
expect(result.current.supportsEIP1559V2).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('editGasMode', () => {
|
||||
it('should return editGasMode passed', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useGasFeeInputs(undefined, undefined, undefined, EDIT_GAS_MODES.SWAPS),
|
||||
);
|
||||
expect(result.current.editGasMode).toBe(EDIT_GAS_MODES.SWAPS);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -34,12 +34,12 @@ const getMaxFeePerGasFromTransaction = (transaction) => {
|
||||
* method to update the setMaxFeePerGas.
|
||||
*/
|
||||
export function useMaxFeePerGasInput({
|
||||
supportsEIP1559V2,
|
||||
estimateToUse,
|
||||
gasEstimateType,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
gasPrice,
|
||||
supportsEIP1559V2,
|
||||
transaction,
|
||||
}) {
|
||||
const supportsEIP1559 =
|
||||
@ -53,7 +53,7 @@ export function useMaxFeePerGasInput({
|
||||
|
||||
const showFiat = useSelector(getShouldShowFiat);
|
||||
|
||||
const maxFeePerGasFromTransaction = supportsEIP1559
|
||||
const initialMaxFeePerGas = supportsEIP1559
|
||||
? getMaxFeePerGasFromTransaction(transaction)
|
||||
: 0;
|
||||
|
||||
@ -61,16 +61,16 @@ export function useMaxFeePerGasInput({
|
||||
// transitional because it is only used to modify a transaction in the
|
||||
// metamask (background) state tree.
|
||||
const [maxFeePerGas, setMaxFeePerGas] = useState(() => {
|
||||
if (maxFeePerGasFromTransaction && feeParamsAreCustom(transaction))
|
||||
return maxFeePerGasFromTransaction;
|
||||
if (initialMaxFeePerGas && feeParamsAreCustom(transaction))
|
||||
return initialMaxFeePerGas;
|
||||
return null;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (supportsEIP1559V2) {
|
||||
setMaxFeePerGas(maxFeePerGasFromTransaction);
|
||||
if (supportsEIP1559V2 && initialMaxFeePerGas) {
|
||||
setMaxFeePerGas(initialMaxFeePerGas);
|
||||
}
|
||||
}, [maxFeePerGasFromTransaction, setMaxFeePerGas, supportsEIP1559V2]);
|
||||
}, [initialMaxFeePerGas, setMaxFeePerGas, supportsEIP1559V2]);
|
||||
|
||||
let gasSettings = {
|
||||
gasLimit: decimalToHex(gasLimit),
|
||||
@ -117,7 +117,7 @@ export function useMaxFeePerGasInput({
|
||||
gasFeeEstimates,
|
||||
gasEstimateType,
|
||||
estimateToUse,
|
||||
maxFeePerGasFromTransaction,
|
||||
initialMaxFeePerGas || 0,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
checkNetworkAndAccountSupports1559,
|
||||
getShouldShowFiat,
|
||||
} from '../../selectors';
|
||||
import { multiplyCurrencies } from '../../../shared/modules/conversion.utils';
|
||||
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
|
||||
import { multiplyCurrencies } from '../../../shared/modules/conversion.utils';
|
||||
|
||||
import { useCurrencyDisplay } from '../useCurrencyDisplay';
|
||||
import { useUserPreferencedCurrency } from '../useUserPreferencedCurrency';
|
||||
@ -34,11 +34,11 @@ const getMaxPriorityFeePerGasFromTransaction = (transaction) => {
|
||||
* method to update the maxPriorityFeePerGas.
|
||||
*/
|
||||
export function useMaxPriorityFeePerGasInput({
|
||||
supportsEIP1559V2,
|
||||
estimateToUse,
|
||||
gasEstimateType,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
supportsEIP1559V2,
|
||||
transaction,
|
||||
}) {
|
||||
const supportsEIP1559 =
|
||||
@ -52,25 +52,21 @@ export function useMaxPriorityFeePerGasInput({
|
||||
|
||||
const showFiat = useSelector(getShouldShowFiat);
|
||||
|
||||
const maxPriorityFeePerGasFromTransaction = supportsEIP1559
|
||||
const initialMaxPriorityFeePerGas = supportsEIP1559
|
||||
? getMaxPriorityFeePerGasFromTransaction(transaction)
|
||||
: 0;
|
||||
|
||||
const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = useState(() => {
|
||||
if (maxPriorityFeePerGasFromTransaction && feeParamsAreCustom(transaction))
|
||||
return maxPriorityFeePerGasFromTransaction;
|
||||
if (initialMaxPriorityFeePerGas && feeParamsAreCustom(transaction))
|
||||
return initialMaxPriorityFeePerGas;
|
||||
return null;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (supportsEIP1559V2) {
|
||||
setMaxPriorityFeePerGas(maxPriorityFeePerGasFromTransaction);
|
||||
if (supportsEIP1559V2 && initialMaxPriorityFeePerGas) {
|
||||
setMaxPriorityFeePerGas(initialMaxPriorityFeePerGas);
|
||||
}
|
||||
}, [
|
||||
maxPriorityFeePerGasFromTransaction,
|
||||
setMaxPriorityFeePerGas,
|
||||
supportsEIP1559V2,
|
||||
]);
|
||||
}, [initialMaxPriorityFeePerGas, setMaxPriorityFeePerGas, supportsEIP1559V2]);
|
||||
|
||||
const maxPriorityFeePerGasToUse =
|
||||
maxPriorityFeePerGas ??
|
||||
@ -79,7 +75,7 @@ export function useMaxPriorityFeePerGasInput({
|
||||
gasFeeEstimates,
|
||||
gasEstimateType,
|
||||
estimateToUse,
|
||||
maxPriorityFeePerGasFromTransaction,
|
||||
initialMaxPriorityFeePerGas || 0,
|
||||
);
|
||||
|
||||
// We need to display the estimated fiat currency impact of the
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { PRIORITY_LEVELS } from '../../../shared/constants/gas';
|
||||
import { EDIT_GAS_MODES, PRIORITY_LEVELS } from '../../../shared/constants/gas';
|
||||
import {
|
||||
decimalToHex,
|
||||
decGWEIToHexWEI,
|
||||
} from '../../helpers/utils/conversions.util';
|
||||
import { updateTransaction as updateTransactionFn } from '../../store/actions';
|
||||
import {
|
||||
updateCustomSwapsEIP1559GasParams,
|
||||
updateSwapsUserFeeLevel,
|
||||
updateTransaction as updateTransactionFn,
|
||||
} from '../../store/actions';
|
||||
|
||||
export const useTransactionFunctions = ({
|
||||
defaultEstimateToUse,
|
||||
editGasMode,
|
||||
gasFeeEstimates,
|
||||
gasLimit: gasLimitInTransaction,
|
||||
transaction,
|
||||
@ -38,16 +43,29 @@ export const useTransactionFunctions = ({
|
||||
|
||||
const updatedTxMeta = {
|
||||
...transaction,
|
||||
userFeeLevel: estimateUsed || 'custom',
|
||||
userFeeLevel: estimateUsed || PRIORITY_LEVELS.CUSTOM,
|
||||
txParams: {
|
||||
...transaction.txParams,
|
||||
...newGasSettings,
|
||||
},
|
||||
};
|
||||
|
||||
dispatch(updateTransactionFn(updatedTxMeta));
|
||||
if (editGasMode === EDIT_GAS_MODES.SWAPS) {
|
||||
dispatch(
|
||||
updateSwapsUserFeeLevel(estimateUsed || PRIORITY_LEVELS.CUSTOM),
|
||||
);
|
||||
dispatch(updateCustomSwapsEIP1559GasParams(newGasSettings));
|
||||
} else {
|
||||
dispatch(updateTransactionFn(updatedTxMeta));
|
||||
}
|
||||
},
|
||||
[defaultEstimateToUse, dispatch, gasLimitInTransaction, transaction],
|
||||
[
|
||||
defaultEstimateToUse,
|
||||
dispatch,
|
||||
editGasMode,
|
||||
gasLimitInTransaction,
|
||||
transaction,
|
||||
],
|
||||
);
|
||||
|
||||
const updateTransactionUsingGasFeeEstimates = useCallback(
|
||||
|
@ -269,8 +269,12 @@ export default function ConfirmApprove() {
|
||||
transaction={transaction}
|
||||
/>
|
||||
)}
|
||||
<EditGasFeePopover />
|
||||
<AdvancedGasFeePopover />
|
||||
{supportsEIP1559V2 && (
|
||||
<>
|
||||
<EditGasFeePopover />
|
||||
<AdvancedGasFeePopover />
|
||||
</>
|
||||
)}
|
||||
</TransactionModalContextProvider>
|
||||
}
|
||||
hideSenderToRecipient
|
||||
|
@ -439,7 +439,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
key="gas_details"
|
||||
hexMaximumTransactionFee={hexMaximumTransactionFee}
|
||||
hexMinimumTransactionFee={hexMinimumTransactionFee}
|
||||
isMainnet={isMainnet}
|
||||
maxFeePerGas={maxFeePerGas}
|
||||
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
||||
supportsEIP1559={supportsEIP1559}
|
||||
|
@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { TYPOGRAPHY } from '../../../../helpers/constants/design-system';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import { getIsMainnet } from '../../../../selectors';
|
||||
import Box from '../../../../components/ui/box';
|
||||
import I18nValue from '../../../../components/ui/i18n-value';
|
||||
import InfoTooltip from '../../../../components/ui/info-tooltip/info-tooltip';
|
||||
import Typography from '../../../../components/ui/typography/typography';
|
||||
|
||||
const GasDetailsItemTitle = () => {
|
||||
const t = useI18nContext();
|
||||
const isMainnet = useSelector(getIsMainnet);
|
||||
|
||||
return (
|
||||
<Box display="flex">
|
||||
<Box marginRight={1}>
|
||||
<I18nValue messageKey="transactionDetailGasHeadingV2" />
|
||||
</Box>
|
||||
<span className="gas-details-item-title__estimate">
|
||||
(<I18nValue messageKey="transactionDetailGasInfoV2" />)
|
||||
</span>
|
||||
<InfoTooltip
|
||||
contentText={
|
||||
<>
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
{t('transactionDetailGasTooltipIntro', [
|
||||
isMainnet ? t('networkNameEthereum') : '',
|
||||
])}
|
||||
</Typography>
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
{t('transactionDetailGasTooltipExplanation')}
|
||||
</Typography>
|
||||
<Typography variant={TYPOGRAPHY.H7}>
|
||||
<a
|
||||
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('transactionDetailGasTooltipConversion')}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
position="bottom"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default GasDetailsItemTitle;
|
@ -0,0 +1,9 @@
|
||||
.gas-details-item-title {
|
||||
&__estimate {
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: $Grey-500;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
|
||||
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network';
|
||||
import { GasFeeContextProvider } from '../../../../contexts/gasFee';
|
||||
import { renderWithProvider } from '../../../../../test/jest';
|
||||
import configureStore from '../../../../store/store';
|
||||
|
||||
import GasDetailsItemTitle from './gas-details-item-title';
|
||||
|
||||
jest.mock('../../../../store/actions', () => ({
|
||||
disconnectGasFeeEstimatePoller: jest.fn(),
|
||||
getGasFeeEstimatesAndStartPolling: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
const render = () => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
provider: { chainId: MAINNET_CHAIN_ID },
|
||||
cachedBalances: {},
|
||||
accounts: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
balance: '0x176e5b6f173ebe66',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
},
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider transaction={{ txParams: {} }}>
|
||||
<GasDetailsItemTitle userAcknowledgedGasMissing={false} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('GasDetailsItem', () => {
|
||||
it('should render label', async () => {
|
||||
render();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Gas')).toBeInTheDocument();
|
||||
expect(screen.queryByText('(estimated)')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { default } from './gas-details-item-title';
|
@ -2,31 +2,27 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||
import { COLORS } from '../../../helpers/constants/design-system';
|
||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||
import { hexWEIToDecGWEI } from '../../../helpers/utils/conversions.util';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
|
||||
import Box from '../../../components/ui/box';
|
||||
import Typography from '../../../components/ui/typography/typography';
|
||||
import GasTiming from '../../../components/app/gas-timing/gas-timing.component';
|
||||
import I18nValue from '../../../components/ui/i18n-value';
|
||||
import InfoTooltip from '../../../components/ui/info-tooltip/info-tooltip';
|
||||
import LoadingHeartBeat from '../../../components/ui/loading-heartbeat';
|
||||
import TransactionDetailItem from '../../../components/app/transaction-detail-item/transaction-detail-item.component';
|
||||
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
||||
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||
import GasDetailsItemTitle from './gas-details-item-title';
|
||||
|
||||
const GasDetailsItem = ({
|
||||
hexMaximumTransactionFee,
|
||||
hexMinimumTransactionFee,
|
||||
isMainnet,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
userAcknowledgedGasMissing,
|
||||
useNativeCurrencyAsPrimaryCurrency,
|
||||
}) => {
|
||||
const t = useI18nContext();
|
||||
const { estimateUsed, hasSimulationError, transaction } = useGasFeeContext();
|
||||
|
||||
if (hasSimulationError && !userAcknowledgedGasMissing) return null;
|
||||
@ -34,40 +30,7 @@ const GasDetailsItem = ({
|
||||
return (
|
||||
<TransactionDetailItem
|
||||
key="gas-item"
|
||||
detailTitle={
|
||||
<Box display="flex">
|
||||
<Box marginRight={1}>
|
||||
<I18nValue messageKey="transactionDetailGasHeadingV2" />
|
||||
</Box>
|
||||
<span className="gas-details-item__estimate">
|
||||
(<I18nValue messageKey="transactionDetailGasInfoV2" />)
|
||||
</span>
|
||||
<InfoTooltip
|
||||
contentText={
|
||||
<>
|
||||
<Typography tag={TYPOGRAPHY.Paragraph} variant={TYPOGRAPHY.H7}>
|
||||
{t('transactionDetailGasTooltipIntro', [
|
||||
isMainnet ? t('networkNameEthereum') : '',
|
||||
])}
|
||||
</Typography>
|
||||
<Typography tag={TYPOGRAPHY.Paragraph} variant={TYPOGRAPHY.H7}>
|
||||
{t('transactionDetailGasTooltipExplanation')}
|
||||
</Typography>
|
||||
<Typography tag={TYPOGRAPHY.Paragraph} variant={TYPOGRAPHY.H7}>
|
||||
<a
|
||||
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('transactionDetailGasTooltipConversion')}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
position="bottom"
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
detailTitle={<GasDetailsItemTitle />}
|
||||
detailTitleColor={COLORS.BLACK}
|
||||
detailText={
|
||||
<div className="gas-details-item__currency-container">
|
||||
@ -137,7 +100,6 @@ const GasDetailsItem = ({
|
||||
GasDetailsItem.propTypes = {
|
||||
hexMaximumTransactionFee: PropTypes.string,
|
||||
hexMinimumTransactionFee: PropTypes.string,
|
||||
isMainnet: PropTypes.bool,
|
||||
maxFeePerGas: PropTypes.string,
|
||||
maxPriorityFeePerGas: PropTypes.string,
|
||||
userAcknowledgedGasMissing: PropTypes.bool.isRequired,
|
||||
|
@ -1,12 +1,4 @@
|
||||
.gas-details-item {
|
||||
&__estimate {
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: $Grey-500;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
&__gas-fee-warning {
|
||||
color: $secondary-1;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
@import 'confirm-decrypt-message/confirm-decrypt-message';
|
||||
@import 'confirm-encryption-public-key/confirm-encryption-public-key';
|
||||
@import 'confirm-transaction-base/gas-details-item/gas-details-item';
|
||||
@import 'confirm-transaction-base/gas-details-item/gas-details-item-title/gas-details-item-title';
|
||||
@import 'confirm-transaction-base/transaction-alerts/transaction-alerts';
|
||||
@import 'confirmation/confirmation';
|
||||
@import 'connected-sites/index';
|
||||
|
@ -1,5 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FeeCard renders the component with EIP-1559 V2 enabled 1`] = `null`;
|
||||
|
||||
exports[`FeeCard renders the component with EIP-1559 enabled 1`] = `null`;
|
||||
|
||||
exports[`FeeCard renders the component with initial props 1`] = `null`;
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
TYPOGRAPHY,
|
||||
FONT_WEIGHT,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import GasDetailsItemTitle from '../../confirm-transaction-base/gas-details-item/gas-details-item-title';
|
||||
|
||||
const GAS_FEES_LEARN_MORE_URL =
|
||||
'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172';
|
||||
@ -34,6 +35,7 @@ export default function FeeCard({
|
||||
onQuotesClick,
|
||||
chainId,
|
||||
isBestQuote,
|
||||
supportsEIP1559V2,
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
@ -73,42 +75,46 @@ export default function FeeCard({
|
||||
<TransactionDetailItem
|
||||
key="gas-item"
|
||||
detailTitle={
|
||||
<>
|
||||
{t('transactionDetailGasHeading')}
|
||||
<InfoTooltip
|
||||
position="top"
|
||||
contentText={
|
||||
<>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
{t('swapGasFeesSummary', [
|
||||
getTranslatedNetworkName(),
|
||||
])}
|
||||
</p>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
{t('swapGasFeesDetails')}
|
||||
</p>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
<a
|
||||
className="fee-card__link"
|
||||
onClick={() => {
|
||||
gasFeesLearnMoreLinkClickedEvent();
|
||||
global.platform.openTab({
|
||||
url: GAS_FEES_LEARN_MORE_URL,
|
||||
});
|
||||
}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('swapGasFeesLearnMore')}
|
||||
</a>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
containerClassName="fee-card__info-tooltip-content-container"
|
||||
wrapperClassName="fee-card__row-label fee-card__info-tooltip-container"
|
||||
wide
|
||||
/>
|
||||
</>
|
||||
supportsEIP1559V2 ? (
|
||||
<GasDetailsItemTitle />
|
||||
) : (
|
||||
<>
|
||||
{t('transactionDetailGasHeading')}
|
||||
<InfoTooltip
|
||||
position="top"
|
||||
contentText={
|
||||
<>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
{t('swapGasFeesSummary', [
|
||||
getTranslatedNetworkName(),
|
||||
])}
|
||||
</p>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
{t('swapGasFeesDetails')}
|
||||
</p>
|
||||
<p className="fee-card__info-tooltip-paragraph">
|
||||
<a
|
||||
className="fee-card__link"
|
||||
onClick={() => {
|
||||
gasFeesLearnMoreLinkClickedEvent();
|
||||
global.platform.openTab({
|
||||
url: GAS_FEES_LEARN_MORE_URL,
|
||||
});
|
||||
}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('swapGasFeesLearnMore')}
|
||||
</a>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
containerClassName="fee-card__info-tooltip-content-container"
|
||||
wrapperClassName="fee-card__row-label fee-card__info-tooltip-container"
|
||||
wide
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
detailText={primaryFee.fee}
|
||||
detailTotal={secondaryFee.fee}
|
||||
@ -124,12 +130,14 @@ export default function FeeCard({
|
||||
{t('maxFee')}
|
||||
</Typography>
|
||||
{`: ${secondaryFee.maxFee}`}
|
||||
<span
|
||||
className="fee-card__edit-link"
|
||||
onClick={() => onFeeCardMaxRowClick()}
|
||||
>
|
||||
{t('edit')}
|
||||
</span>
|
||||
{!supportsEIP1559V2 && (
|
||||
<span
|
||||
className="fee-card__edit-link"
|
||||
onClick={() => onFeeCardMaxRowClick()}
|
||||
>
|
||||
{t('edit')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -203,4 +211,5 @@ FeeCard.propTypes = {
|
||||
numberOfQuotes: PropTypes.number.isRequired,
|
||||
chainId: PropTypes.string.isRequired,
|
||||
isBestQuote: PropTypes.bool.isRequired,
|
||||
supportsEIP1559V2: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
@ -3,25 +3,37 @@ import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
|
||||
import {
|
||||
getGasEstimateType,
|
||||
getGasFeeEstimates,
|
||||
getIsGasEstimatesLoading,
|
||||
} from '../../../ducks/metamask/metamask';
|
||||
|
||||
import {
|
||||
renderWithProvider,
|
||||
createSwapsMockStore,
|
||||
setBackgroundConnection,
|
||||
MOCKS,
|
||||
} from '../../../../test/jest';
|
||||
import { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
|
||||
import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
|
||||
|
||||
import {
|
||||
checkNetworkAndAccountSupports1559,
|
||||
getPreferences,
|
||||
getSelectedAccount,
|
||||
} from '../../../selectors';
|
||||
import {
|
||||
getGasEstimateType,
|
||||
getGasFeeEstimates,
|
||||
getIsGasEstimatesLoading,
|
||||
} from '../../../ducks/metamask/metamask';
|
||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../helpers/constants/transactions';
|
||||
import { useGasFeeEstimates } from '../../../hooks/useGasFeeEstimates';
|
||||
|
||||
import FeeCard from '.';
|
||||
|
||||
const middleware = [thunk];
|
||||
|
||||
jest.mock('../../../hooks/useGasFeeEstimates', () => ({
|
||||
useGasFeeEstimates: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-redux', () => {
|
||||
const actual = jest.requireActual('react-redux');
|
||||
|
||||
@ -78,6 +90,7 @@ const createProps = (customProps = {}) => {
|
||||
tokenConversionRate: 0.015,
|
||||
chainId: MAINNET_CHAIN_ID,
|
||||
networkAndAccountSupports1559: false,
|
||||
supportsEIP1559V2: false,
|
||||
...customProps,
|
||||
};
|
||||
};
|
||||
@ -118,4 +131,55 @@ describe('FeeCard', () => {
|
||||
document.querySelector('.fee-card__top-bordered-row'),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders the component with EIP-1559 V2 enabled', () => {
|
||||
process.env.EIP_1559_V2 = true;
|
||||
useGasFeeEstimates.mockImplementation(() => ({ gasFeeEstimates: {} }));
|
||||
useSelector.mockImplementation((selector) => {
|
||||
if (selector === getPreferences) {
|
||||
return {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
};
|
||||
}
|
||||
if (selector === getSelectedAccount) {
|
||||
return {
|
||||
balance: '0x440aa47cc2556',
|
||||
};
|
||||
}
|
||||
if (selector === checkNetworkAndAccountSupports1559) {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||
const props = createProps({
|
||||
networkAndAccountSupports1559: true,
|
||||
maxPriorityFeePerGasDecGWEI: '3',
|
||||
maxFeePerGasDecGWEI: '4',
|
||||
supportsEIP1559V2: true,
|
||||
});
|
||||
const { getByText } = renderWithProvider(
|
||||
<GasFeeContextProvider
|
||||
transaction={{ txParams: {}, userFeeLevel: 'high' }}
|
||||
editGasMode={EDIT_GAS_MODES.SWAPS}
|
||||
>
|
||||
<FeeCard {...props} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
expect(getByText('Best of 6 quotes.')).toBeInTheDocument();
|
||||
expect(getByText('Gas')).toBeInTheDocument();
|
||||
expect(getByText('(estimated)')).toBeInTheDocument();
|
||||
expect(getByText('Swap suggested')).toBeInTheDocument();
|
||||
expect(getByText('Max fee')).toBeInTheDocument();
|
||||
expect(getByText(props.primaryFee.fee)).toBeInTheDocument();
|
||||
expect(getByText(props.secondaryFee.fee)).toBeInTheDocument();
|
||||
expect(getByText(`: ${props.secondaryFee.maxFee}`)).toBeInTheDocument();
|
||||
expect(getByText('Includes a 0.875% MetaMask fee.')).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector('.fee-card__top-bordered-row'),
|
||||
).toMatchSnapshot();
|
||||
process.env.EIP_1559_V2 = false;
|
||||
});
|
||||
});
|
||||
|
@ -81,6 +81,10 @@ import {
|
||||
decGWEIToHexWEI,
|
||||
addHexes,
|
||||
} from '../../../helpers/utils/conversions.util';
|
||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||
import { TransactionModalContextProvider } from '../../../contexts/transaction-modal';
|
||||
import AdvancedGasFeePopover from '../../../components/app/advanced-gas-fee-popover';
|
||||
import EditGasFeePopover from '../../../components/app/edit-gas-fee-popover';
|
||||
import MainQuoteSummary from '../main-quote-summary';
|
||||
import { calcGasTotal } from '../../send/send.utils';
|
||||
import { getCustomTxParamsData } from '../../confirm-approve/confirm-approve.util';
|
||||
@ -99,6 +103,10 @@ import CountdownTimer from '../countdown-timer';
|
||||
import SwapsFooter from '../swaps-footer';
|
||||
import ViewQuotePriceDifference from './view-quote-price-difference';
|
||||
|
||||
// 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 ViewQuote() {
|
||||
const history = useHistory();
|
||||
const dispatch = useDispatch();
|
||||
@ -672,93 +680,152 @@ export default function ViewQuote() {
|
||||
}
|
||||
}, [dispatch, viewQuotePageLoadedEvent, reviewSwapClickedTimestamp]);
|
||||
|
||||
const transaction = {
|
||||
userFeeLevel: swapsUserFeeLevel || GAS_RECOMMENDATIONS.HIGH,
|
||||
txParams: {
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
gas: maxGasLimit,
|
||||
},
|
||||
};
|
||||
|
||||
const supportsEIP1559V2 =
|
||||
EIP_1559_V2_ENABLED && networkAndAccountSupports1559;
|
||||
|
||||
return (
|
||||
<div className="view-quote">
|
||||
<div
|
||||
className={classnames('view-quote__content', {
|
||||
'view-quote__content_modal': disableSubmissionDueToPriceWarning,
|
||||
})}
|
||||
>
|
||||
{selectQuotePopoverShown && (
|
||||
<SelectQuotePopover
|
||||
quoteDataRows={renderablePopoverData}
|
||||
onClose={() => setSelectQuotePopoverShown(false)}
|
||||
onSubmit={(aggId) => dispatch(swapsQuoteSelected(aggId))}
|
||||
swapToSymbol={destinationTokenSymbol}
|
||||
initialAggId={usedQuote.aggregator}
|
||||
onQuoteDetailsIsOpened={quoteDetailsOpened}
|
||||
/>
|
||||
)}
|
||||
<GasFeeContextProvider
|
||||
editGasMode={EDIT_GAS_MODES.SWAPS}
|
||||
minimumGasLimit={usedGasLimit}
|
||||
transaction={transaction}
|
||||
>
|
||||
<TransactionModalContextProvider captureEventEnabled={false}>
|
||||
<div className="view-quote">
|
||||
<div
|
||||
className={classnames('view-quote__content', {
|
||||
'view-quote__content_modal': disableSubmissionDueToPriceWarning,
|
||||
})}
|
||||
>
|
||||
{selectQuotePopoverShown && (
|
||||
<SelectQuotePopover
|
||||
quoteDataRows={renderablePopoverData}
|
||||
onClose={() => setSelectQuotePopoverShown(false)}
|
||||
onSubmit={(aggId) => dispatch(swapsQuoteSelected(aggId))}
|
||||
swapToSymbol={destinationTokenSymbol}
|
||||
initialAggId={usedQuote.aggregator}
|
||||
onQuoteDetailsIsOpened={quoteDetailsOpened}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showEditGasPopover && networkAndAccountSupports1559 && (
|
||||
<EditGasPopover
|
||||
transaction={{
|
||||
userFeeLevel: swapsUserFeeLevel || GAS_RECOMMENDATIONS.HIGH,
|
||||
txParams: {
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
gas: maxGasLimit,
|
||||
},
|
||||
}}
|
||||
minimumGasLimit={usedGasLimit}
|
||||
defaultEstimateToUse={GAS_RECOMMENDATIONS.HIGH}
|
||||
mode={EDIT_GAS_MODES.SWAPS}
|
||||
confirmButtonText={t('submit')}
|
||||
onClose={onCloseEditGasPopover}
|
||||
/>
|
||||
)}
|
||||
{!supportsEIP1559V2 &&
|
||||
showEditGasPopover &&
|
||||
networkAndAccountSupports1559 && (
|
||||
<EditGasPopover
|
||||
transaction={transaction}
|
||||
minimumGasLimit={usedGasLimit}
|
||||
defaultEstimateToUse={GAS_RECOMMENDATIONS.HIGH}
|
||||
mode={EDIT_GAS_MODES.SWAPS}
|
||||
confirmButtonText={t('submit')}
|
||||
onClose={onCloseEditGasPopover}
|
||||
/>
|
||||
)}
|
||||
{supportsEIP1559V2 && (
|
||||
<>
|
||||
<EditGasFeePopover />
|
||||
<AdvancedGasFeePopover />
|
||||
</>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={classnames('view-quote__warning-wrapper', {
|
||||
'view-quote__warning-wrapper--thin': !isShowingWarning,
|
||||
})}
|
||||
>
|
||||
{viewQuotePriceDifferenceComponent}
|
||||
{(showInsufficientWarning || tokenBalanceUnavailable) && (
|
||||
<ActionableMessage
|
||||
message={actionableBalanceErrorMessage}
|
||||
onClose={() => setWarningHidden(true)}
|
||||
<div
|
||||
className={classnames('view-quote__warning-wrapper', {
|
||||
'view-quote__warning-wrapper--thin': !isShowingWarning,
|
||||
})}
|
||||
>
|
||||
{viewQuotePriceDifferenceComponent}
|
||||
{(showInsufficientWarning || tokenBalanceUnavailable) && (
|
||||
<ActionableMessage
|
||||
message={actionableBalanceErrorMessage}
|
||||
onClose={() => setWarningHidden(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="view-quote__countdown-timer-container">
|
||||
<CountdownTimer
|
||||
timeStarted={quotesLastFetched}
|
||||
warningTime="0:30"
|
||||
labelKey="swapNewQuoteIn"
|
||||
/>
|
||||
</div>
|
||||
<MainQuoteSummary
|
||||
sourceValue={calcTokenValue(
|
||||
sourceTokenValue,
|
||||
sourceTokenDecimals,
|
||||
)}
|
||||
sourceDecimals={sourceTokenDecimals}
|
||||
sourceSymbol={sourceTokenSymbol}
|
||||
destinationValue={calcTokenValue(
|
||||
destinationTokenValue,
|
||||
destinationTokenDecimals,
|
||||
)}
|
||||
destinationDecimals={destinationTokenDecimals}
|
||||
destinationSymbol={destinationTokenSymbol}
|
||||
sourceIconUrl={sourceTokenIconUrl}
|
||||
destinationIconUrl={destinationIconUrl}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="view-quote__countdown-timer-container">
|
||||
<CountdownTimer
|
||||
timeStarted={quotesLastFetched}
|
||||
warningTime="0:30"
|
||||
labelKey="swapNewQuoteIn"
|
||||
/>
|
||||
</div>
|
||||
<MainQuoteSummary
|
||||
sourceValue={calcTokenValue(sourceTokenValue, sourceTokenDecimals)}
|
||||
sourceDecimals={sourceTokenDecimals}
|
||||
sourceSymbol={sourceTokenSymbol}
|
||||
destinationValue={calcTokenValue(
|
||||
destinationTokenValue,
|
||||
destinationTokenDecimals,
|
||||
)}
|
||||
destinationDecimals={destinationTokenDecimals}
|
||||
destinationSymbol={destinationTokenSymbol}
|
||||
sourceIconUrl={sourceTokenIconUrl}
|
||||
destinationIconUrl={destinationIconUrl}
|
||||
/>
|
||||
<div
|
||||
className={classnames('view-quote__fee-card-container', {
|
||||
'view-quote__fee-card-container--three-rows':
|
||||
approveTxParams && (!balanceError || warningHidden),
|
||||
})}
|
||||
>
|
||||
<FeeCard
|
||||
primaryFee={{
|
||||
fee: feeInEth,
|
||||
maxFee: maxFeeInEth,
|
||||
<div
|
||||
className={classnames('view-quote__fee-card-container', {
|
||||
'view-quote__fee-card-container--three-rows':
|
||||
approveTxParams && (!balanceError || warningHidden),
|
||||
})}
|
||||
>
|
||||
<FeeCard
|
||||
primaryFee={{
|
||||
fee: feeInEth,
|
||||
maxFee: maxFeeInEth,
|
||||
}}
|
||||
secondaryFee={{
|
||||
fee: feeInFiat,
|
||||
maxFee: maxFeeInFiat,
|
||||
}}
|
||||
onFeeCardMaxRowClick={onFeeCardMaxRowClick}
|
||||
hideTokenApprovalRow={
|
||||
!approveTxParams || (balanceError && !warningHidden)
|
||||
}
|
||||
tokenApprovalSourceTokenSymbol={sourceTokenSymbol}
|
||||
onTokenApprovalClick={onFeeCardTokenApprovalClick}
|
||||
metaMaskFee={String(metaMaskFee)}
|
||||
numberOfQuotes={Object.values(quotes).length}
|
||||
onQuotesClick={() => {
|
||||
allAvailableQuotesOpened();
|
||||
setSelectQuotePopoverShown(true);
|
||||
}}
|
||||
chainId={chainId}
|
||||
isBestQuote={isBestQuote}
|
||||
supportsEIP1559V2={supportsEIP1559V2}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SwapsFooter
|
||||
onSubmit={() => {
|
||||
setSubmitClicked(true);
|
||||
if (!balanceError) {
|
||||
dispatch(signAndSendTransactions(history, metaMetricsEvent));
|
||||
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
||||
history.push(DEFAULT_ROUTE);
|
||||
} else {
|
||||
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
||||
}
|
||||
}}
|
||||
secondaryFee={{
|
||||
fee: feeInFiat,
|
||||
maxFee: maxFeeInFiat,
|
||||
}}
|
||||
onFeeCardMaxRowClick={onFeeCardMaxRowClick}
|
||||
hideTokenApprovalRow={
|
||||
!approveTxParams || (balanceError && !warningHidden)
|
||||
submitText={t('swap')}
|
||||
hideCancel
|
||||
disabled={
|
||||
submitClicked ||
|
||||
balanceError ||
|
||||
tokenBalanceUnavailable ||
|
||||
disableSubmissionDueToPriceWarning ||
|
||||
(networkAndAccountSupports1559 &&
|
||||
baseAndPriorityFeePerGas === undefined) ||
|
||||
(!networkAndAccountSupports1559 &&
|
||||
(gasPrice === null || gasPrice === undefined))
|
||||
}
|
||||
tokenApprovalSourceTokenSymbol={sourceTokenSymbol}
|
||||
onTokenApprovalClick={onFeeCardTokenApprovalClick}
|
||||
@ -772,33 +839,7 @@ export default function ViewQuote() {
|
||||
isBestQuote={isBestQuote}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SwapsFooter
|
||||
onSubmit={() => {
|
||||
setSubmitClicked(true);
|
||||
if (!balanceError) {
|
||||
dispatch(signAndSendTransactions(history, metaMetricsEvent));
|
||||
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
||||
history.push(DEFAULT_ROUTE);
|
||||
} else {
|
||||
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
||||
}
|
||||
}}
|
||||
submitText={t('swap')}
|
||||
hideCancel
|
||||
disabled={
|
||||
submitClicked ||
|
||||
balanceError ||
|
||||
tokenBalanceUnavailable ||
|
||||
disableSubmissionDueToPriceWarning ||
|
||||
(networkAndAccountSupports1559 &&
|
||||
baseAndPriorityFeePerGas === undefined) ||
|
||||
(!networkAndAccountSupports1559 &&
|
||||
(gasPrice === null || gasPrice === undefined))
|
||||
}
|
||||
className={isShowingWarning && 'view-quote__thin-swaps-footer'}
|
||||
showTopBorder
|
||||
/>
|
||||
</div>
|
||||
</TransactionModalContextProvider>
|
||||
</GasFeeContextProvider>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user