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 { PageContainerFooter } from '../../../ui/page-container';
|
||||||
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from '.';
|
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 {
|
export default class ConfirmPageContainerContent extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
@ -42,7 +39,8 @@ export default class ConfirmPageContainerContent extends Component {
|
|||||||
hideUserAcknowledgedGasMissing: PropTypes.bool,
|
hideUserAcknowledgedGasMissing: PropTypes.bool,
|
||||||
unapprovedTxCount: PropTypes.number,
|
unapprovedTxCount: PropTypes.number,
|
||||||
rejectNText: PropTypes.string,
|
rejectNText: PropTypes.string,
|
||||||
hideTitle: PropTypes.boolean,
|
hideTitle: PropTypes.bool,
|
||||||
|
supportsEIP1559V2: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderContent() {
|
renderContent() {
|
||||||
@ -101,6 +99,7 @@ export default class ConfirmPageContainerContent extends Component {
|
|||||||
hideTitle,
|
hideTitle,
|
||||||
setUserAcknowledgedGasMissing,
|
setUserAcknowledgedGasMissing,
|
||||||
hideUserAcknowledgedGasMissing,
|
hideUserAcknowledgedGasMissing,
|
||||||
|
supportsEIP1559V2,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const primaryAction = hideUserAcknowledgedGasMissing
|
const primaryAction = hideUserAcknowledgedGasMissing
|
||||||
@ -141,11 +140,13 @@ export default class ConfirmPageContainerContent extends Component {
|
|||||||
hideTitle={hideTitle}
|
hideTitle={hideTitle}
|
||||||
/>
|
/>
|
||||||
{this.renderContent()}
|
{this.renderContent()}
|
||||||
{!EIP_1559_V2 && !hasSimulationError && (errorKey || errorMessage) && (
|
{!supportsEIP1559V2 &&
|
||||||
<div className="confirm-page-container-content__error-container">
|
!hasSimulationError &&
|
||||||
<ErrorMessage errorMessage={errorMessage} errorKey={errorKey} />
|
(errorKey || errorMessage) && (
|
||||||
</div>
|
<div className="confirm-page-container-content__error-container">
|
||||||
)}
|
<ErrorMessage errorMessage={errorMessage} errorKey={errorKey} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<PageContainerFooter
|
<PageContainerFooter
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
cancelText={cancelText}
|
cancelText={cancelText}
|
||||||
|
@ -41,6 +41,8 @@ describe('Confirm Page Container Content', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('render ConfirmPageContainer component with simulation error', async () => {
|
it('render ConfirmPageContainer component with simulation error', async () => {
|
||||||
|
process.env.EIP_1559_V2 = false;
|
||||||
|
|
||||||
const { queryByText, getByText } = renderWithProvider(
|
const { queryByText, getByText } = renderWithProvider(
|
||||||
<ConfirmPageContainerContent {...props} />,
|
<ConfirmPageContainerContent {...props} />,
|
||||||
store,
|
store,
|
||||||
|
@ -3,9 +3,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Identicon from '../../../../ui/identicon';
|
import Identicon from '../../../../ui/identicon';
|
||||||
|
import { useGasFeeContext } from '../../../../../contexts/gasFee';
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
|
||||||
|
|
||||||
const ConfirmPageContainerSummary = (props) => {
|
const ConfirmPageContainerSummary = (props) => {
|
||||||
const {
|
const {
|
||||||
@ -21,6 +19,8 @@ const ConfirmPageContainerSummary = (props) => {
|
|||||||
hideTitle,
|
hideTitle,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const { supportsEIP1559V2 } = useGasFeeContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('confirm-page-container-summary', className)}>
|
<div className={classnames('confirm-page-container-summary', className)}>
|
||||||
{origin === 'metamask' ? null : (
|
{origin === 'metamask' ? null : (
|
||||||
@ -48,7 +48,7 @@ const ConfirmPageContainerSummary = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{!hideSubtitle && !EIP_1559_V2 && (
|
{!hideSubtitle && !supportsEIP1559V2 && (
|
||||||
<div className="confirm-page-container-summary__subtitle">
|
<div className="confirm-page-container-summary__subtitle">
|
||||||
{subtitleComponent}
|
{subtitleComponent}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
|
||||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||||
import ErrorMessage from '../../ui/error-message';
|
|
||||||
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
|
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
|
||||||
|
|
||||||
|
import { PageContainerFooter } from '../../ui/page-container';
|
||||||
import Dialog from '../../ui/dialog';
|
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 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 {
|
import {
|
||||||
ConfirmPageContainerHeader,
|
ConfirmPageContainerHeader,
|
||||||
ConfirmPageContainerContent,
|
ConfirmPageContainerContent,
|
||||||
ConfirmPageContainerNavigation,
|
ConfirmPageContainerNavigation,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
|
||||||
|
|
||||||
export default class ConfirmPageContainer extends Component {
|
export default class ConfirmPageContainer extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
@ -77,6 +78,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
showAddToAddressBookModal: PropTypes.func,
|
showAddToAddressBookModal: PropTypes.func,
|
||||||
contact: PropTypes.object,
|
contact: PropTypes.object,
|
||||||
isOwnedAccount: PropTypes.bool,
|
isOwnedAccount: PropTypes.bool,
|
||||||
|
supportsEIP1559V2: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -127,6 +129,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
showAddToAddressBookModal,
|
showAddToAddressBookModal,
|
||||||
contact = {},
|
contact = {},
|
||||||
isOwnedAccount,
|
isOwnedAccount,
|
||||||
|
supportsEIP1559V2,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const showAddToAddressDialog =
|
const showAddToAddressDialog =
|
||||||
@ -208,6 +211,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
origin={origin}
|
origin={origin}
|
||||||
ethGasPriceWarning={ethGasPriceWarning}
|
ethGasPriceWarning={ethGasPriceWarning}
|
||||||
hideTitle={hideTitle}
|
hideTitle={hideTitle}
|
||||||
|
supportsEIP1559V2={supportsEIP1559V2}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{shouldDisplayWarning && (
|
{shouldDisplayWarning && (
|
||||||
@ -230,7 +234,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
)}
|
)}
|
||||||
</PageContainerFooter>
|
</PageContainerFooter>
|
||||||
)}
|
)}
|
||||||
{editingGas && !EIP_1559_V2 && (
|
{editingGas && !supportsEIP1559V2 && (
|
||||||
<EditGasPopover
|
<EditGasPopover
|
||||||
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
|
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
|
||||||
onClose={handleCloseEditGas}
|
onClose={handleCloseEditGas}
|
||||||
|
@ -28,8 +28,6 @@ import { useGasFeeContext } from '../../../contexts/gasFee';
|
|||||||
|
|
||||||
// Once we reach this second threshold, we switch to minutes as a unit
|
// Once we reach this second threshold, we switch to minutes as a unit
|
||||||
const SECOND_CUTOFF = 90;
|
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"
|
// Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes"
|
||||||
const toHumanReadableTime = (milliseconds = 1, t) => {
|
const toHumanReadableTime = (milliseconds = 1, t) => {
|
||||||
@ -50,7 +48,7 @@ export default function GasTiming({
|
|||||||
|
|
||||||
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
||||||
const t = useContext(I18nContext);
|
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,
|
// 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
|
// 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;
|
let unknownProcessingTimeText;
|
||||||
if (EIP_1559_V2) {
|
if (supportsEIP1559V2) {
|
||||||
unknownProcessingTimeText = t('editGasTooLow');
|
unknownProcessingTimeText = t('editGasTooLow');
|
||||||
} else {
|
} else {
|
||||||
unknownProcessingTimeText = (
|
unknownProcessingTimeText = (
|
||||||
@ -155,7 +153,7 @@ export default function GasTiming({
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!EIP_1559_V2 || estimateUsed === 'low') {
|
if (!supportsEIP1559V2 || estimateUsed === 'low') {
|
||||||
attitude = 'negative';
|
attitude = 'negative';
|
||||||
}
|
}
|
||||||
// If the user has chosen a value less than our low estimate,
|
// 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
|
// code below needs to cleaned-up once EIP_1559_V2 flag is removed
|
||||||
else if (EIP_1559_V2) {
|
else if (supportsEIP1559V2) {
|
||||||
text = t('gasTimingNegative', [
|
text = t('gasTimingNegative', [
|
||||||
toHumanReadableTime(low.maxWaitTimeEstimate, t),
|
toHumanReadableTime(low.maxWaitTimeEstimate, t),
|
||||||
]);
|
]);
|
||||||
@ -199,8 +197,8 @@ export default function GasTiming({
|
|||||||
<Typography
|
<Typography
|
||||||
variant={TYPOGRAPHY.H7}
|
variant={TYPOGRAPHY.H7}
|
||||||
className={classNames('gas-timing', {
|
className={classNames('gas-timing', {
|
||||||
[`gas-timing--${attitude}`]: attitude && !EIP_1559_V2,
|
[`gas-timing--${attitude}`]: attitude && !supportsEIP1559V2,
|
||||||
[`gas-timing--${attitude}-V2`]: attitude && EIP_1559_V2,
|
[`gas-timing--${attitude}-V2`]: attitude && supportsEIP1559V2,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
@ -16,9 +16,6 @@ export default function TransactionDetail({
|
|||||||
onEdit,
|
onEdit,
|
||||||
userAcknowledgedGasMissing,
|
userAcknowledgedGasMissing,
|
||||||
}) {
|
}) {
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
const EIP_1559_V2 = process.env.EIP_1559_V2;
|
|
||||||
|
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const {
|
const {
|
||||||
gasLimit,
|
gasLimit,
|
||||||
@ -26,11 +23,12 @@ export default function TransactionDetail({
|
|||||||
estimateUsed,
|
estimateUsed,
|
||||||
maxFeePerGas,
|
maxFeePerGas,
|
||||||
maxPriorityFeePerGas,
|
maxPriorityFeePerGas,
|
||||||
|
supportsEIP1559V2,
|
||||||
transaction,
|
transaction,
|
||||||
} = useGasFeeContext();
|
} = useGasFeeContext();
|
||||||
const { openModal } = useTransactionModalContext();
|
const { openModal } = useTransactionModalContext();
|
||||||
|
|
||||||
if (EIP_1559_V2 && estimateUsed) {
|
if (supportsEIP1559V2 && estimateUsed) {
|
||||||
const editEnabled = !hasSimulationError || userAcknowledgedGasMissing;
|
const editEnabled = !hasSimulationError || userAcknowledgedGasMissing;
|
||||||
if (!editEnabled) return null;
|
if (!editEnabled) return null;
|
||||||
|
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { screen } from '@testing-library/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 { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||||
import { renderWithProvider } from '../../../../test/jest';
|
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 configureStore from '../../../store/store';
|
||||||
|
|
||||||
import TransactionDetail from './transaction-detail.component';
|
import TransactionDetail from './transaction-detail.component';
|
||||||
@ -19,20 +23,14 @@ jest.mock('../../../store/actions', () => ({
|
|||||||
const render = ({ componentProps, contextProps } = {}) => {
|
const render = ({ componentProps, contextProps } = {}) => {
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
metamask: {
|
metamask: {
|
||||||
nativeCurrency: ETH,
|
...mockState.metamask,
|
||||||
preferences: {
|
|
||||||
useNativeCurrencyAsPrimaryCurrency: true,
|
|
||||||
},
|
|
||||||
provider: {},
|
|
||||||
cachedBalances: {},
|
|
||||||
accounts: {
|
accounts: {
|
||||||
'0xAddress': {
|
[mockState.metamask.selectedAddress]: {
|
||||||
address: '0xAddress',
|
address: mockState.metamask.selectedAddress,
|
||||||
balance: '0x176e5b6f173ebe66',
|
balance: '0x1F4',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selectedAddress: '0xAddress',
|
gasFeeEstimates: mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET],
|
||||||
featureFlags: { advancedInlineGas: true },
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -129,4 +127,17 @@ describe('TransactionDetail', () => {
|
|||||||
expect(screen.queryByRole('button')).toBeInTheDocument();
|
expect(screen.queryByRole('button')).toBeInTheDocument();
|
||||||
expect(screen.queryByText('Low')).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',
|
minimumGasLimit = '0x5208',
|
||||||
editGasMode = EDIT_GAS_MODES.MODIFY_IN_PLACE,
|
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 =
|
const supportsEIP1559 =
|
||||||
useSelector(checkNetworkAndAccountSupports1559) &&
|
useSelector(checkNetworkAndAccountSupports1559) &&
|
||||||
!isLegacyTransaction(transaction?.txParams);
|
!isLegacyTransaction(transaction?.txParams);
|
||||||
@ -304,6 +308,7 @@ export function useGasFeeInputs(
|
|||||||
hasGasErrors,
|
hasGasErrors,
|
||||||
hasSimulationError,
|
hasSimulationError,
|
||||||
supportsEIP1559,
|
supportsEIP1559,
|
||||||
|
supportsEIP1559V2: supportsEIP1559 && EIP_1559_V2_ENABLED,
|
||||||
updateTransactionUsingGasFeeEstimates,
|
updateTransactionUsingGasFeeEstimates,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -316,4 +316,38 @@ describe('useGasFeeInputs', () => {
|
|||||||
expect(result.current.estimatedMinimumFiat).toBe('');
|
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,
|
TRANSACTION_STATUSES,
|
||||||
} from '../../../shared/constants/transaction';
|
} from '../../../shared/constants/transaction';
|
||||||
import { getMethodName } from '../../helpers/utils/metrics';
|
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 { toBuffer } from '../../../shared/modules/buffer-utils';
|
||||||
|
|
||||||
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
||||||
@ -57,7 +60,8 @@ import GasDetailsItem from './gas-details-item';
|
|||||||
import TransactionAlerts from './transaction-alerts';
|
import TransactionAlerts from './transaction-alerts';
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
// 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 = () =>
|
const renderHeartBeatIfNotInTest = () =>
|
||||||
process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />;
|
process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />;
|
||||||
@ -420,7 +424,9 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const renderGasDetailsItem = () => {
|
const renderGasDetailsItem = () => {
|
||||||
return EIP_1559_V2 ? (
|
return EIP_1559_V2_ENABLED &&
|
||||||
|
supportsEIP1559 &&
|
||||||
|
!isLegacyTransaction(txData) ? (
|
||||||
<GasDetailsItem
|
<GasDetailsItem
|
||||||
key="gas_details"
|
key="gas_details"
|
||||||
hexMaximumTransactionFee={hexMaximumTransactionFee}
|
hexMaximumTransactionFee={hexMaximumTransactionFee}
|
||||||
@ -571,14 +577,12 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="confirm-page-container-content__details">
|
<div className="confirm-page-container-content__details">
|
||||||
{EIP_1559_V2 && (
|
<TransactionAlerts
|
||||||
<TransactionAlerts
|
setUserAcknowledgedGasMissing={() =>
|
||||||
setUserAcknowledgedGasMissing={() =>
|
this.setUserAcknowledgedGasMissing()
|
||||||
this.setUserAcknowledgedGasMissing()
|
}
|
||||||
}
|
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<TransactionDetail
|
<TransactionDetail
|
||||||
disabled={isDisabled()}
|
disabled={isDisabled()}
|
||||||
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
|
||||||
@ -949,6 +953,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
gasFeeIsCustom,
|
gasFeeIsCustom,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
hardwareWalletRequiresConnection,
|
hardwareWalletRequiresConnection,
|
||||||
|
supportsEIP1559,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
submitting,
|
submitting,
|
||||||
@ -1046,6 +1051,11 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
editingGas={editingGas}
|
editingGas={editingGas}
|
||||||
handleCloseEditGas={() => this.handleCloseEditGas()}
|
handleCloseEditGas={() => this.handleCloseEditGas()}
|
||||||
currentTransaction={txData}
|
currentTransaction={txData}
|
||||||
|
supportsEIP1559V2={
|
||||||
|
EIP_1559_V2_ENABLED &&
|
||||||
|
supportsEIP1559 &&
|
||||||
|
!isLegacyTransaction(txData)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</TransactionModalContextProvider>
|
</TransactionModalContextProvider>
|
||||||
);
|
);
|
||||||
|
@ -27,11 +27,10 @@ const GasDetailsItem = ({
|
|||||||
maxFeePerGas,
|
maxFeePerGas,
|
||||||
maxPriorityFeePerGas,
|
maxPriorityFeePerGas,
|
||||||
userAcknowledgedGasMissing,
|
userAcknowledgedGasMissing,
|
||||||
txData,
|
|
||||||
useNativeCurrencyAsPrimaryCurrency,
|
useNativeCurrencyAsPrimaryCurrency,
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const { estimateUsed, hasSimulationError } = useGasFeeContext();
|
const { estimateUsed, hasSimulationError, transaction } = useGasFeeContext();
|
||||||
|
|
||||||
if (hasSimulationError && !userAcknowledgedGasMissing) return null;
|
if (hasSimulationError && !userAcknowledgedGasMissing) return null;
|
||||||
|
|
||||||
@ -124,10 +123,10 @@ const GasDetailsItem = ({
|
|||||||
subTitle={
|
subTitle={
|
||||||
<GasTiming
|
<GasTiming
|
||||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||||
maxPriorityFeePerGas || txData.txParams.maxPriorityFeePerGas,
|
maxPriorityFeePerGas || transaction.txParams.maxPriorityFeePerGas,
|
||||||
)}
|
)}
|
||||||
maxFeePerGas={hexWEIToDecGWEI(
|
maxFeePerGas={hexWEIToDecGWEI(
|
||||||
maxFeePerGas || txData.txParams.maxFeePerGas,
|
maxFeePerGas || transaction.txParams.maxFeePerGas,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@ -142,7 +141,6 @@ GasDetailsItem.propTypes = {
|
|||||||
maxFeePerGas: PropTypes.string,
|
maxFeePerGas: PropTypes.string,
|
||||||
maxPriorityFeePerGas: PropTypes.string,
|
maxPriorityFeePerGas: PropTypes.string,
|
||||||
userAcknowledgedGasMissing: PropTypes.bool.isRequired,
|
userAcknowledgedGasMissing: PropTypes.bool.isRequired,
|
||||||
txData: PropTypes.object,
|
|
||||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,12 +37,8 @@ const render = ({ componentProps, contextProps } = {}) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return renderWithProvider(
|
return renderWithProvider(
|
||||||
<GasFeeContextProvider {...contextProps}>
|
<GasFeeContextProvider transaction={{ txParams: {} }} {...contextProps}>
|
||||||
<GasDetailsItem
|
<GasDetailsItem userAcknowledgedGasMissing={false} {...componentProps} />
|
||||||
txData={{ txParams: {} }}
|
|
||||||
userAcknowledgedGasMissing={false}
|
|
||||||
{...componentProps}
|
|
||||||
/>
|
|
||||||
</GasFeeContextProvider>,
|
</GasFeeContextProvider>,
|
||||||
store,
|
store,
|
||||||
);
|
);
|
||||||
@ -60,14 +56,18 @@ describe('GasDetailsItem', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show warning icon if estimates are high', async () => {
|
it('should show warning icon if estimates are high', async () => {
|
||||||
render({ contextProps: { defaultEstimateToUse: 'high' } });
|
render({
|
||||||
|
contextProps: { transaction: { txParams: {}, userFeeLevel: 'high' } },
|
||||||
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('⚠ Max fee:')).toBeInTheDocument();
|
expect(screen.queryByText('⚠ Max fee:')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show warning icon if estimates are not high', async () => {
|
it('should not show warning icon if estimates are not high', async () => {
|
||||||
render({ contextProps: { defaultEstimateToUse: 'low' } });
|
render({
|
||||||
|
contextProps: { transaction: { txParams: {}, userFeeLevel: 'low' } },
|
||||||
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
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', () => {
|
it('should return null if there is simulationError and user has not acknowledged gasMissing warning', () => {
|
||||||
const { container } = render({
|
const { container } = render({
|
||||||
contextProps: {
|
contextProps: {
|
||||||
defaultEstimateToUse: 'low',
|
transaction: {
|
||||||
transaction: { simulationFails: true },
|
txParams: {},
|
||||||
|
simulationFails: true,
|
||||||
|
userFeeLevel: 'low',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(container.innerHTML).toHaveLength(0);
|
expect(container.innerHTML).toHaveLength(0);
|
||||||
|
@ -16,10 +16,17 @@ const TransactionAlerts = ({
|
|||||||
userAcknowledgedGasMissing,
|
userAcknowledgedGasMissing,
|
||||||
setUserAcknowledgedGasMissing,
|
setUserAcknowledgedGasMissing,
|
||||||
}) => {
|
}) => {
|
||||||
const { balanceError, estimateUsed, hasSimulationError } = useGasFeeContext();
|
const {
|
||||||
|
balanceError,
|
||||||
|
estimateUsed,
|
||||||
|
hasSimulationError,
|
||||||
|
supportsEIP1559V2,
|
||||||
|
} = useGasFeeContext();
|
||||||
const pendingTransactions = useSelector(submittedPendingTransactionsSelector);
|
const pendingTransactions = useSelector(submittedPendingTransactionsSelector);
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
|
|
||||||
|
if (!supportsEIP1559V2) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="transaction-alerts">
|
<div className="transaction-alerts">
|
||||||
{hasSimulationError && (
|
{hasSimulationError && (
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { screen } from '@testing-library/react';
|
import { fireEvent, screen } from '@testing-library/react';
|
||||||
|
|
||||||
import { ETH } from '../../../helpers/constants/common';
|
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
||||||
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
|
import {
|
||||||
|
TRANSACTION_ENVELOPE_TYPES,
|
||||||
|
TRANSACTION_STATUSES,
|
||||||
|
} from '../../../../shared/constants/transaction';
|
||||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
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 { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||||
import configureStore from '../../../store/store';
|
import configureStore from '../../../store/store';
|
||||||
|
|
||||||
@ -17,44 +22,54 @@ jest.mock('../../../store/actions', () => ({
|
|||||||
addPollingTokenToAppState: jest.fn(),
|
addPollingTokenToAppState: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const render = ({ props, state } = {}) => {
|
const render = ({ componentProps, transactionProps, state }) => {
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
metamask: {
|
metamask: {
|
||||||
nativeCurrency: ETH,
|
...mockState.metamask,
|
||||||
preferences: {
|
|
||||||
useNativeCurrencyAsPrimaryCurrency: true,
|
|
||||||
},
|
|
||||||
provider: {},
|
|
||||||
cachedBalances: {},
|
|
||||||
accounts: {
|
accounts: {
|
||||||
'0xAddress': {
|
[mockState.metamask.selectedAddress]: {
|
||||||
address: '0xAddress',
|
address: mockState.metamask.selectedAddress,
|
||||||
balance: '0x1F4',
|
balance: '0x1F4',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selectedAddress: '0xAddress',
|
gasFeeEstimates: mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET],
|
||||||
...state,
|
...state,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return renderWithProvider(
|
return renderWithProvider(
|
||||||
<GasFeeContextProvider {...props}>
|
<GasFeeContextProvider
|
||||||
<TransactionAlerts />
|
transaction={{
|
||||||
|
txParams: {
|
||||||
|
type: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
|
||||||
|
},
|
||||||
|
...transactionProps,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TransactionAlerts {...componentProps} />
|
||||||
</GasFeeContextProvider>,
|
</GasFeeContextProvider>,
|
||||||
store,
|
store,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('TransactionAlerts', () => {
|
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', () => {
|
it('should returning warning message for low gas estimate', () => {
|
||||||
render({ props: { transaction: { userFeeLevel: 'low' } } });
|
render({ transactionProps: { userFeeLevel: 'low' } });
|
||||||
expect(
|
expect(
|
||||||
document.getElementsByClassName('actionable-message--warning'),
|
document.getElementsByClassName('actionable-message--warning'),
|
||||||
).toHaveLength(1);
|
).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null for gas estimate other than low', () => {
|
it('should return null for gas estimate other than low', () => {
|
||||||
render({ props: { transaction: { userFeeLevel: 'high' } } });
|
render({ transactionProps: { userFeeLevel: 'high' } });
|
||||||
expect(
|
expect(
|
||||||
document.getElementsByClassName('actionable-message--warning'),
|
document.getElementsByClassName('actionable-message--warning'),
|
||||||
).toHaveLength(0);
|
).toHaveLength(0);
|
||||||
@ -62,8 +77,9 @@ describe('TransactionAlerts', () => {
|
|||||||
|
|
||||||
it('should not show insufficient balance message if transaction value is less than balance', () => {
|
it('should not show insufficient balance message if transaction value is less than balance', () => {
|
||||||
render({
|
render({
|
||||||
props: {
|
transactionProps: {
|
||||||
transaction: { userFeeLevel: 'high', txParams: { value: '0x64' } },
|
userFeeLevel: 'high',
|
||||||
|
txParams: { value: '0x64' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(screen.queryByText('Insufficient funds.')).not.toBeInTheDocument();
|
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', () => {
|
it('should show insufficient balance message if transaction value is more than balance', () => {
|
||||||
render({
|
render({
|
||||||
props: {
|
transactionProps: {
|
||||||
transaction: { userFeeLevel: 'high', txParams: { value: '0x5208' } },
|
userFeeLevel: 'high',
|
||||||
|
txParams: { value: '0x5208' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(screen.queryByText('Insufficient funds.')).toBeInTheDocument();
|
expect(screen.queryByText('Insufficient funds.')).toBeInTheDocument();
|
||||||
@ -86,7 +103,7 @@ describe('TransactionAlerts', () => {
|
|||||||
id: 0,
|
id: 0,
|
||||||
time: 0,
|
time: 0,
|
||||||
txParams: {
|
txParams: {
|
||||||
from: '0xAddress',
|
from: mockState.metamask.selectedAddress,
|
||||||
to: '0xRecipient',
|
to: '0xRecipient',
|
||||||
},
|
},
|
||||||
status: TRANSACTION_STATUSES.SUBMITTED,
|
status: TRANSACTION_STATUSES.SUBMITTED,
|
||||||
@ -98,4 +115,58 @@ describe('TransactionAlerts', () => {
|
|||||||
screen.queryByText('You have (1) pending transaction.'),
|
screen.queryByText('You have (1) pending transaction.'),
|
||||||
).toBeInTheDocument();
|
).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