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:
parent
a6bb503e52
commit
6fc9b6b7a2
@ -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';
|
||||
|
73
ui/components/app/edit-gas-fee-button/edit-gas-fee-button.js
Normal file
73
ui/components/app/edit-gas-fee-button/edit-gas-fee-button.js
Normal 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,
|
||||
};
|
@ -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();
|
||||
});
|
||||
});
|
1
ui/components/app/edit-gas-fee-button/index.js
Normal file
1
ui/components/app/edit-gas-fee-button/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './edit-gas-fee-button';
|
48
ui/components/app/edit-gas-fee-button/index.scss
Normal file
48
ui/components/app/edit-gas-fee-button/index.scss
Normal 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%;
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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' }),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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: {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user