mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Legacy transactions to use old transaction flow (#12782)
This commit is contained in:
parent
e8b7fcf8dc
commit
0e0e7ac830
27
test/data/mock-estimates.json
Normal file
27
test/data/mock-estimates.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"fee-market": {
|
||||
"gasEstimateType": "fee-market",
|
||||
"gasFeeEstimates": {
|
||||
"low": {
|
||||
"minWaitTimeEstimate": 180000,
|
||||
"maxWaitTimeEstimate": 300000,
|
||||
"suggestedMaxPriorityFeePerGas": "3",
|
||||
"suggestedMaxFeePerGas": "53"
|
||||
},
|
||||
"medium": {
|
||||
"minWaitTimeEstimate": 15000,
|
||||
"maxWaitTimeEstimate": 60000,
|
||||
"suggestedMaxPriorityFeePerGas": "7",
|
||||
"suggestedMaxFeePerGas": "70"
|
||||
},
|
||||
"high": {
|
||||
"minWaitTimeEstimate": 0,
|
||||
"maxWaitTimeEstimate": 15000,
|
||||
"suggestedMaxPriorityFeePerGas": "10",
|
||||
"suggestedMaxFeePerGas": "100"
|
||||
},
|
||||
"estimatedBaseFee": "50"
|
||||
},
|
||||
"estimatedGasFeeTimeBounds": {}
|
||||
}
|
||||
}
|
@ -7,9 +7,6 @@ import ActionableMessage from '../../../ui/actionable-message/actionable-message
|
||||
import { PageContainerFooter } from '../../../ui/page-container';
|
||||
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from '.';
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
|
||||
export default class ConfirmPageContainerContent extends Component {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
@ -42,7 +39,8 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
hideUserAcknowledgedGasMissing: PropTypes.bool,
|
||||
unapprovedTxCount: PropTypes.number,
|
||||
rejectNText: PropTypes.string,
|
||||
hideTitle: PropTypes.boolean,
|
||||
hideTitle: PropTypes.bool,
|
||||
supportsEIP1559V2: PropTypes.bool,
|
||||
};
|
||||
|
||||
renderContent() {
|
||||
@ -101,6 +99,7 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
hideTitle,
|
||||
setUserAcknowledgedGasMissing,
|
||||
hideUserAcknowledgedGasMissing,
|
||||
supportsEIP1559V2,
|
||||
} = this.props;
|
||||
|
||||
const primaryAction = hideUserAcknowledgedGasMissing
|
||||
@ -141,11 +140,13 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
hideTitle={hideTitle}
|
||||
/>
|
||||
{this.renderContent()}
|
||||
{!EIP_1559_V2 && !hasSimulationError && (errorKey || errorMessage) && (
|
||||
<div className="confirm-page-container-content__error-container">
|
||||
<ErrorMessage errorMessage={errorMessage} errorKey={errorKey} />
|
||||
</div>
|
||||
)}
|
||||
{!supportsEIP1559V2 &&
|
||||
!hasSimulationError &&
|
||||
(errorKey || errorMessage) && (
|
||||
<div className="confirm-page-container-content__error-container">
|
||||
<ErrorMessage errorMessage={errorMessage} errorKey={errorKey} />
|
||||
</div>
|
||||
)}
|
||||
<PageContainerFooter
|
||||
onCancel={onCancel}
|
||||
cancelText={cancelText}
|
||||
|
@ -41,6 +41,8 @@ describe('Confirm Page Container Content', () => {
|
||||
});
|
||||
|
||||
it('render ConfirmPageContainer component with simulation error', async () => {
|
||||
process.env.EIP_1559_V2 = false;
|
||||
|
||||
const { queryByText, getByText } = renderWithProvider(
|
||||
<ConfirmPageContainerContent {...props} />,
|
||||
store,
|
||||
|
@ -3,9 +3,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import Identicon from '../../../../ui/identicon';
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
import { useGasFeeContext } from '../../../../../contexts/gasFee';
|
||||
|
||||
const ConfirmPageContainerSummary = (props) => {
|
||||
const {
|
||||
@ -21,6 +19,8 @@ const ConfirmPageContainerSummary = (props) => {
|
||||
hideTitle,
|
||||
} = props;
|
||||
|
||||
const { supportsEIP1559V2 } = useGasFeeContext();
|
||||
|
||||
return (
|
||||
<div className={classnames('confirm-page-container-summary', className)}>
|
||||
{origin === 'metamask' ? null : (
|
||||
@ -48,7 +48,7 @@ const ConfirmPageContainerSummary = (props) => {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{!hideSubtitle && !EIP_1559_V2 && (
|
||||
{!hideSubtitle && !supportsEIP1559V2 && (
|
||||
<div className="confirm-page-container-summary__subtitle">
|
||||
{subtitleComponent}
|
||||
</div>
|
||||
|
@ -1,24 +1,25 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import SenderToRecipient from '../../ui/sender-to-recipient';
|
||||
import { PageContainerFooter } from '../../ui/page-container';
|
||||
import EditGasPopover from '../edit-gas-popover';
|
||||
|
||||
import { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
|
||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||
import ErrorMessage from '../../ui/error-message';
|
||||
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
|
||||
|
||||
import { PageContainerFooter } from '../../ui/page-container';
|
||||
import Dialog from '../../ui/dialog';
|
||||
import ErrorMessage from '../../ui/error-message';
|
||||
import SenderToRecipient from '../../ui/sender-to-recipient';
|
||||
|
||||
import AdvancedGasFeePopover from '../advanced-gas-fee-popover';
|
||||
import EditGasFeePopover from '../edit-gas-fee-popover';
|
||||
import EditGasFeePopover from '../edit-gas-fee-popover/edit-gas-fee-popover';
|
||||
import EditGasPopover from '../edit-gas-popover';
|
||||
|
||||
import {
|
||||
ConfirmPageContainerHeader,
|
||||
ConfirmPageContainerContent,
|
||||
ConfirmPageContainerNavigation,
|
||||
} from '.';
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
|
||||
export default class ConfirmPageContainer extends Component {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
@ -77,6 +78,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
showAddToAddressBookModal: PropTypes.func,
|
||||
contact: PropTypes.object,
|
||||
isOwnedAccount: PropTypes.bool,
|
||||
supportsEIP1559V2: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -127,6 +129,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
showAddToAddressBookModal,
|
||||
contact = {},
|
||||
isOwnedAccount,
|
||||
supportsEIP1559V2,
|
||||
} = this.props;
|
||||
|
||||
const showAddToAddressDialog =
|
||||
@ -208,6 +211,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
origin={origin}
|
||||
ethGasPriceWarning={ethGasPriceWarning}
|
||||
hideTitle={hideTitle}
|
||||
supportsEIP1559V2={supportsEIP1559V2}
|
||||
/>
|
||||
)}
|
||||
{shouldDisplayWarning && (
|
||||
@ -230,7 +234,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
)}
|
||||
</PageContainerFooter>
|
||||
)}
|
||||
{editingGas && !EIP_1559_V2 && (
|
||||
{editingGas && !supportsEIP1559V2 && (
|
||||
<EditGasPopover
|
||||
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
|
||||
onClose={handleCloseEditGas}
|
||||
|
@ -28,8 +28,6 @@ import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||
|
||||
// Once we reach this second threshold, we switch to minutes as a unit
|
||||
const SECOND_CUTOFF = 90;
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
|
||||
// Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes"
|
||||
const toHumanReadableTime = (milliseconds = 1, t) => {
|
||||
@ -50,7 +48,7 @@ export default function GasTiming({
|
||||
|
||||
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
||||
const t = useContext(I18nContext);
|
||||
const { estimateUsed } = useGasFeeContext();
|
||||
const { estimateUsed, supportsEIP1559V2 } = useGasFeeContext();
|
||||
|
||||
// If the user has chosen a value lower than the low gas fee estimate,
|
||||
// We'll need to use the useEffect hook below to make a call to calculate
|
||||
@ -97,7 +95,7 @@ export default function GasTiming({
|
||||
]);
|
||||
|
||||
let unknownProcessingTimeText;
|
||||
if (EIP_1559_V2) {
|
||||
if (supportsEIP1559V2) {
|
||||
unknownProcessingTimeText = t('editGasTooLow');
|
||||
} else {
|
||||
unknownProcessingTimeText = (
|
||||
@ -155,7 +153,7 @@ export default function GasTiming({
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if (!EIP_1559_V2 || estimateUsed === 'low') {
|
||||
if (!supportsEIP1559V2 || estimateUsed === 'low') {
|
||||
attitude = 'negative';
|
||||
}
|
||||
// If the user has chosen a value less than our low estimate,
|
||||
@ -176,7 +174,7 @@ export default function GasTiming({
|
||||
}
|
||||
}
|
||||
// code below needs to cleaned-up once EIP_1559_V2 flag is removed
|
||||
else if (EIP_1559_V2) {
|
||||
else if (supportsEIP1559V2) {
|
||||
text = t('gasTimingNegative', [
|
||||
toHumanReadableTime(low.maxWaitTimeEstimate, t),
|
||||
]);
|
||||
@ -199,8 +197,8 @@ export default function GasTiming({
|
||||
<Typography
|
||||
variant={TYPOGRAPHY.H7}
|
||||
className={classNames('gas-timing', {
|
||||
[`gas-timing--${attitude}`]: attitude && !EIP_1559_V2,
|
||||
[`gas-timing--${attitude}-V2`]: attitude && EIP_1559_V2,
|
||||
[`gas-timing--${attitude}`]: attitude && !supportsEIP1559V2,
|
||||
[`gas-timing--${attitude}-V2`]: attitude && supportsEIP1559V2,
|
||||
})}
|
||||
>
|
||||
{text}
|
||||
|
@ -16,9 +16,6 @@ export default function TransactionDetail({
|
||||
onEdit,
|
||||
userAcknowledgedGasMissing,
|
||||
}) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
|
||||
const t = useI18nContext();
|
||||
const {
|
||||
gasLimit,
|
||||
@ -26,11 +23,12 @@ export default function TransactionDetail({
|
||||
estimateUsed,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
supportsEIP1559V2,
|
||||
transaction,
|
||||
} = useGasFeeContext();
|
||||
const { openModal } = useTransactionModalContext();
|
||||
|
||||
if (EIP_1559_V2 && estimateUsed) {
|
||||
if (supportsEIP1559V2 && estimateUsed) {
|
||||
const editEnabled = !hasSimulationError || userAcknowledgedGasMissing;
|
||||
if (!editEnabled) return null;
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { ETH } from '../../../helpers/constants/common';
|
||||
import { GAS_ESTIMATE_TYPES } 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 TransactionDetail from './transaction-detail.component';
|
||||
@ -19,20 +23,14 @@ jest.mock('../../../store/actions', () => ({
|
||||
const render = ({ componentProps, contextProps } = {}) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
provider: {},
|
||||
cachedBalances: {},
|
||||
...mockState.metamask,
|
||||
accounts: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
balance: '0x176e5b6f173ebe66',
|
||||
[mockState.metamask.selectedAddress]: {
|
||||
address: mockState.metamask.selectedAddress,
|
||||
balance: '0x1F4',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
featureFlags: { advancedInlineGas: true },
|
||||
gasFeeEstimates: mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET],
|
||||
},
|
||||
});
|
||||
|
||||
@ -129,4 +127,17 @@ describe('TransactionDetail', () => {
|
||||
expect(screen.queryByRole('button')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Low')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render edit link with text edit for legacy transactions', () => {
|
||||
render({
|
||||
contextProps: {
|
||||
transaction: {
|
||||
userFeeLevel: 'low',
|
||||
txParams: { type: TRANSACTION_ENVELOPE_TYPES.LEGACY },
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(screen.queryByText('🐢')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Edit')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -74,6 +74,10 @@ export function useGasFeeInputs(
|
||||
minimumGasLimit = '0x5208',
|
||||
editGasMode = EDIT_GAS_MODES.MODIFY_IN_PLACE,
|
||||
) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2_ENABLED =
|
||||
process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true';
|
||||
|
||||
const supportsEIP1559 =
|
||||
useSelector(checkNetworkAndAccountSupports1559) &&
|
||||
!isLegacyTransaction(transaction?.txParams);
|
||||
@ -304,6 +308,7 @@ export function useGasFeeInputs(
|
||||
hasGasErrors,
|
||||
hasSimulationError,
|
||||
supportsEIP1559,
|
||||
supportsEIP1559V2: supportsEIP1559 && EIP_1559_V2_ENABLED,
|
||||
updateTransactionUsingGasFeeEstimates,
|
||||
};
|
||||
}
|
||||
|
@ -316,4 +316,38 @@ describe('useGasFeeInputs', () => {
|
||||
expect(result.current.estimatedMinimumFiat).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('supportsEIP1559V2', () => {
|
||||
beforeEach(() => {
|
||||
configureEIP1559();
|
||||
useSelector.mockImplementation(
|
||||
generateUseSelectorRouter({
|
||||
checkNetworkAndAccountSupports1559Response: true,
|
||||
}),
|
||||
);
|
||||
process.env.EIP_1559_V2 = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env.EIP_1559_V2 = false;
|
||||
});
|
||||
|
||||
it('return true for fee_market transaction type', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useGasFeeInputs(null, {
|
||||
txParams: { type: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET },
|
||||
}),
|
||||
);
|
||||
expect(result.current.supportsEIP1559V2).toBe(true);
|
||||
});
|
||||
|
||||
it('return false for legacy transaction type', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useGasFeeInputs(null, {
|
||||
txParams: { type: TRANSACTION_ENVELOPE_TYPES.LEGACY },
|
||||
}),
|
||||
);
|
||||
expect(result.current.supportsEIP1559V2).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -26,7 +26,10 @@ import {
|
||||
TRANSACTION_STATUSES,
|
||||
} from '../../../shared/constants/transaction';
|
||||
import { getMethodName } from '../../helpers/utils/metrics';
|
||||
import { getTransactionTypeTitle } from '../../helpers/utils/transactions.util';
|
||||
import {
|
||||
getTransactionTypeTitle,
|
||||
isLegacyTransaction,
|
||||
} from '../../helpers/utils/transactions.util';
|
||||
import { toBuffer } from '../../../shared/modules/buffer-utils';
|
||||
|
||||
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
||||
@ -57,7 +60,8 @@ import GasDetailsItem from './gas-details-item';
|
||||
import TransactionAlerts from './transaction-alerts';
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
||||
const EIP_1559_V2_ENABLED =
|
||||
process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true';
|
||||
|
||||
const renderHeartBeatIfNotInTest = () =>
|
||||
process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />;
|
||||
@ -420,7 +424,9 @@ export default class ConfirmTransactionBase extends Component {
|
||||
) : null;
|
||||
|
||||
const renderGasDetailsItem = () => {
|
||||
return EIP_1559_V2 ? (
|
||||
return EIP_1559_V2_ENABLED &&
|
||||
supportsEIP1559 &&
|
||||
!isLegacyTransaction(txData) ? (
|
||||
<GasDetailsItem
|
||||
key="gas_details"
|
||||
hexMaximumTransactionFee={hexMaximumTransactionFee}
|
||||
@ -571,14 +577,12 @@ export default class ConfirmTransactionBase extends Component {
|
||||
|
||||
return (
|
||||
<div className="confirm-page-container-content__details">
|
||||
{EIP_1559_V2 && (
|
||||
<TransactionAlerts
|
||||
setUserAcknowledgedGasMissing={() =>
|
||||
this.setUserAcknowledgedGasMissing()
|
||||
}
|
||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||
/>
|
||||
)}
|
||||
<TransactionAlerts
|
||||
setUserAcknowledgedGasMissing={() =>
|
||||
this.setUserAcknowledgedGasMissing()
|
||||
}
|
||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||
/>
|
||||
<TransactionDetail
|
||||
disabled={isDisabled()}
|
||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||
@ -949,6 +953,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
gasFeeIsCustom,
|
||||
nativeCurrency,
|
||||
hardwareWalletRequiresConnection,
|
||||
supportsEIP1559,
|
||||
} = this.props;
|
||||
const {
|
||||
submitting,
|
||||
@ -1046,6 +1051,11 @@ export default class ConfirmTransactionBase extends Component {
|
||||
editingGas={editingGas}
|
||||
handleCloseEditGas={() => this.handleCloseEditGas()}
|
||||
currentTransaction={txData}
|
||||
supportsEIP1559V2={
|
||||
EIP_1559_V2_ENABLED &&
|
||||
supportsEIP1559 &&
|
||||
!isLegacyTransaction(txData)
|
||||
}
|
||||
/>
|
||||
</TransactionModalContextProvider>
|
||||
);
|
||||
|
@ -27,11 +27,10 @@ const GasDetailsItem = ({
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
userAcknowledgedGasMissing,
|
||||
txData,
|
||||
useNativeCurrencyAsPrimaryCurrency,
|
||||
}) => {
|
||||
const t = useI18nContext();
|
||||
const { estimateUsed, hasSimulationError } = useGasFeeContext();
|
||||
const { estimateUsed, hasSimulationError, transaction } = useGasFeeContext();
|
||||
|
||||
if (hasSimulationError && !userAcknowledgedGasMissing) return null;
|
||||
|
||||
@ -124,10 +123,10 @@ const GasDetailsItem = ({
|
||||
subTitle={
|
||||
<GasTiming
|
||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||
maxPriorityFeePerGas || txData.txParams.maxPriorityFeePerGas,
|
||||
maxPriorityFeePerGas || transaction.txParams.maxPriorityFeePerGas,
|
||||
)}
|
||||
maxFeePerGas={hexWEIToDecGWEI(
|
||||
maxFeePerGas || txData.txParams.maxFeePerGas,
|
||||
maxFeePerGas || transaction.txParams.maxFeePerGas,
|
||||
)}
|
||||
/>
|
||||
}
|
||||
@ -142,7 +141,6 @@ GasDetailsItem.propTypes = {
|
||||
maxFeePerGas: PropTypes.string,
|
||||
maxPriorityFeePerGas: PropTypes.string,
|
||||
userAcknowledgedGasMissing: PropTypes.bool.isRequired,
|
||||
txData: PropTypes.object,
|
||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
@ -37,12 +37,8 @@ const render = ({ componentProps, contextProps } = {}) => {
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider {...contextProps}>
|
||||
<GasDetailsItem
|
||||
txData={{ txParams: {} }}
|
||||
userAcknowledgedGasMissing={false}
|
||||
{...componentProps}
|
||||
/>
|
||||
<GasFeeContextProvider transaction={{ txParams: {} }} {...contextProps}>
|
||||
<GasDetailsItem userAcknowledgedGasMissing={false} {...componentProps} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
@ -60,14 +56,18 @@ describe('GasDetailsItem', () => {
|
||||
});
|
||||
|
||||
it('should show warning icon if estimates are high', async () => {
|
||||
render({ contextProps: { defaultEstimateToUse: 'high' } });
|
||||
render({
|
||||
contextProps: { transaction: { txParams: {}, userFeeLevel: 'high' } },
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('⚠ Max fee:')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show warning icon if estimates are not high', async () => {
|
||||
render({ contextProps: { defaultEstimateToUse: 'low' } });
|
||||
render({
|
||||
contextProps: { transaction: { txParams: {}, userFeeLevel: 'low' } },
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||
});
|
||||
@ -76,8 +76,11 @@ describe('GasDetailsItem', () => {
|
||||
it('should return null if there is simulationError and user has not acknowledged gasMissing warning', () => {
|
||||
const { container } = render({
|
||||
contextProps: {
|
||||
defaultEstimateToUse: 'low',
|
||||
transaction: { simulationFails: true },
|
||||
transaction: {
|
||||
txParams: {},
|
||||
simulationFails: true,
|
||||
userFeeLevel: 'low',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(container.innerHTML).toHaveLength(0);
|
||||
|
@ -16,10 +16,17 @@ const TransactionAlerts = ({
|
||||
userAcknowledgedGasMissing,
|
||||
setUserAcknowledgedGasMissing,
|
||||
}) => {
|
||||
const { balanceError, estimateUsed, hasSimulationError } = useGasFeeContext();
|
||||
const {
|
||||
balanceError,
|
||||
estimateUsed,
|
||||
hasSimulationError,
|
||||
supportsEIP1559V2,
|
||||
} = useGasFeeContext();
|
||||
const pendingTransactions = useSelector(submittedPendingTransactionsSelector);
|
||||
const t = useI18nContext();
|
||||
|
||||
if (!supportsEIP1559V2) return null;
|
||||
|
||||
return (
|
||||
<div className="transaction-alerts">
|
||||
{hasSimulationError && (
|
||||
|
@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
|
||||
import { ETH } from '../../../helpers/constants/common';
|
||||
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
|
||||
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
||||
import {
|
||||
TRANSACTION_ENVELOPE_TYPES,
|
||||
TRANSACTION_STATUSES,
|
||||
} from '../../../../shared/constants/transaction';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import mockEstimates from '../../../../test/data/mock-estimates.json';
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||
import configureStore from '../../../store/store';
|
||||
|
||||
@ -17,44 +22,54 @@ jest.mock('../../../store/actions', () => ({
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
}));
|
||||
|
||||
const render = ({ props, state } = {}) => {
|
||||
const render = ({ componentProps, transactionProps, state }) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
provider: {},
|
||||
cachedBalances: {},
|
||||
...mockState.metamask,
|
||||
accounts: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
[mockState.metamask.selectedAddress]: {
|
||||
address: mockState.metamask.selectedAddress,
|
||||
balance: '0x1F4',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
gasFeeEstimates: mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET],
|
||||
...state,
|
||||
},
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider {...props}>
|
||||
<TransactionAlerts />
|
||||
<GasFeeContextProvider
|
||||
transaction={{
|
||||
txParams: {
|
||||
type: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
|
||||
},
|
||||
...transactionProps,
|
||||
}}
|
||||
>
|
||||
<TransactionAlerts {...componentProps} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('TransactionAlerts', () => {
|
||||
beforeEach(() => {
|
||||
process.env.EIP_1559_V2 = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env.EIP_1559_V2 = false;
|
||||
});
|
||||
|
||||
it('should returning warning message for low gas estimate', () => {
|
||||
render({ props: { transaction: { userFeeLevel: 'low' } } });
|
||||
render({ transactionProps: { userFeeLevel: 'low' } });
|
||||
expect(
|
||||
document.getElementsByClassName('actionable-message--warning'),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should return null for gas estimate other than low', () => {
|
||||
render({ props: { transaction: { userFeeLevel: 'high' } } });
|
||||
render({ transactionProps: { userFeeLevel: 'high' } });
|
||||
expect(
|
||||
document.getElementsByClassName('actionable-message--warning'),
|
||||
).toHaveLength(0);
|
||||
@ -62,8 +77,9 @@ describe('TransactionAlerts', () => {
|
||||
|
||||
it('should not show insufficient balance message if transaction value is less than balance', () => {
|
||||
render({
|
||||
props: {
|
||||
transaction: { userFeeLevel: 'high', txParams: { value: '0x64' } },
|
||||
transactionProps: {
|
||||
userFeeLevel: 'high',
|
||||
txParams: { value: '0x64' },
|
||||
},
|
||||
});
|
||||
expect(screen.queryByText('Insufficient funds.')).not.toBeInTheDocument();
|
||||
@ -71,8 +87,9 @@ describe('TransactionAlerts', () => {
|
||||
|
||||
it('should show insufficient balance message if transaction value is more than balance', () => {
|
||||
render({
|
||||
props: {
|
||||
transaction: { userFeeLevel: 'high', txParams: { value: '0x5208' } },
|
||||
transactionProps: {
|
||||
userFeeLevel: 'high',
|
||||
txParams: { value: '0x5208' },
|
||||
},
|
||||
});
|
||||
expect(screen.queryByText('Insufficient funds.')).toBeInTheDocument();
|
||||
@ -86,7 +103,7 @@ describe('TransactionAlerts', () => {
|
||||
id: 0,
|
||||
time: 0,
|
||||
txParams: {
|
||||
from: '0xAddress',
|
||||
from: mockState.metamask.selectedAddress,
|
||||
to: '0xRecipient',
|
||||
},
|
||||
status: TRANSACTION_STATUSES.SUBMITTED,
|
||||
@ -98,4 +115,58 @@ describe('TransactionAlerts', () => {
|
||||
screen.queryByText('You have (1) pending transaction.'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('SimulationError Message', () => {
|
||||
it('should show simulation error message along with option to proceed anyway if transaction.simulationFails is true', () => {
|
||||
render({ transactionProps: { simulationFails: true } });
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('I want to proceed anyway'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show options to acknowledge gas-missing warning if component prop userAcknowledgedGasMissing is already true', () => {
|
||||
render({
|
||||
componentProps: {
|
||||
userAcknowledgedGasMissing: true,
|
||||
},
|
||||
transactionProps: { simulationFails: true },
|
||||
});
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('I want to proceed anyway'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call prop setUserAcknowledgedGasMissing if option to acknowledge gas-missing warning is clicked', () => {
|
||||
const setUserAcknowledgedGasMissing = jest.fn();
|
||||
render({
|
||||
componentProps: {
|
||||
setUserAcknowledgedGasMissing,
|
||||
},
|
||||
transactionProps: { simulationFails: true },
|
||||
});
|
||||
fireEvent.click(screen.queryByText('I want to proceed anyway'));
|
||||
expect(setUserAcknowledgedGasMissing).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return null for legacy transactions', () => {
|
||||
const { container } = render({
|
||||
transactionProps: {
|
||||
txParams: {
|
||||
type: TRANSACTION_ENVELOPE_TYPES.LEGACY,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user