mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 09:23:21 +01:00
Edit gas fee modal more changes (#12660)
This commit is contained in:
parent
d6d35d9acb
commit
2c6fb06114
@ -581,6 +581,9 @@
|
||||
"dappSuggested": {
|
||||
"message": "Site suggested"
|
||||
},
|
||||
"dappSuggestedShortLabel": {
|
||||
"message": "Site"
|
||||
},
|
||||
"dappSuggestedTooltip": {
|
||||
"message": "$1 has recommended this price.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
@ -1035,9 +1038,6 @@
|
||||
"gasPriceInfoTooltipContent": {
|
||||
"message": "Gas price specifies the amount of Ether you are willing to pay for each unit of gas."
|
||||
},
|
||||
"gasPriceLabel": {
|
||||
"message": "Gas price"
|
||||
},
|
||||
"gasTimingHoursShort": {
|
||||
"message": "$1 hrs",
|
||||
"description": "$1 represents a number of hours"
|
||||
|
@ -30,6 +30,17 @@ export const GAS_RECOMMENDATIONS = {
|
||||
HIGH: 'high',
|
||||
};
|
||||
|
||||
/**
|
||||
* These represent types of gas estimation
|
||||
*/
|
||||
export const PRIORITY_LEVELS = {
|
||||
LOW: 'low',
|
||||
MEDIUM: 'medium',
|
||||
HIGH: 'high',
|
||||
CUSTOM: 'custom',
|
||||
DAPP_SUGGESTED: 'dappSuggested',
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the user customizing their gas preference
|
||||
*/
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { PRIORITY_LEVELS } from '../../../../shared/constants/gas';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import Popover from '../../ui/popover';
|
||||
import I18nValue from '../../ui/i18n-value';
|
||||
@ -32,9 +33,27 @@ const EditGasFeePopover = ({ onClose }) => {
|
||||
<I18nValue messageKey="maxFee" />
|
||||
</span>
|
||||
</div>
|
||||
<EditGasItem estimateType="low" onClose={onClose} />
|
||||
<EditGasItem estimateType="medium" onClose={onClose} />
|
||||
<EditGasItem estimateType="high" onClose={onClose} />
|
||||
<EditGasItem
|
||||
priorityLevel={PRIORITY_LEVELS.LOW}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<EditGasItem
|
||||
priorityLevel={PRIORITY_LEVELS.MEDIUM}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<EditGasItem
|
||||
priorityLevel={PRIORITY_LEVELS.HIGH}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<div className="edit-gas-fee-popover__content__separator" />
|
||||
<EditGasItem
|
||||
priorityLevel={PRIORITY_LEVELS.DAPP_SUGGESTED}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<EditGasItem
|
||||
priorityLevel={PRIORITY_LEVELS.CUSTOM}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -71,16 +71,18 @@ describe('EditGasFeePopover', () => {
|
||||
expect(screen.queryByText('🐢')).toBeInTheDocument();
|
||||
expect(screen.queryByText('🦊')).toBeInTheDocument();
|
||||
expect(screen.queryByText('🦍')).toBeInTheDocument();
|
||||
expect(screen.queryByText('🌐')).toBeInTheDocument();
|
||||
expect(screen.queryByText('⚙')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Low')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Market')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Aggressive')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Site')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Advanced')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show time estimates', () => {
|
||||
renderComponent();
|
||||
console.log(document.body.innerHTML);
|
||||
expect(screen.queryByText('6 min')).toBeInTheDocument();
|
||||
expect(screen.queryByText('30 sec')).toBeInTheDocument();
|
||||
expect(screen.queryAllByText('5 min')).toHaveLength(2);
|
||||
expect(screen.queryByText('15 sec')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
@ -1,14 +1,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getMaximumGasTotalInHexWei } from '../../../../../shared/modules/gas.utils';
|
||||
import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas';
|
||||
import { PRIORITY_LEVEL_ICON_MAP } from '../../../../helpers/constants/gas';
|
||||
import { PRIMARY } from '../../../../helpers/constants/common';
|
||||
import {
|
||||
decGWEIToHexWEI,
|
||||
decimalToHex,
|
||||
hexWEIToDecGWEI,
|
||||
} from '../../../../helpers/utils/conversions.util';
|
||||
import { getAdvancedGasFeeValues } from '../../../../selectors';
|
||||
import { toHumanReadableTime } from '../../../../helpers/utils/util';
|
||||
import { useGasFeeContext } from '../../../../contexts/gasFee';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
@ -16,59 +20,121 @@ import I18nValue from '../../../ui/i18n-value';
|
||||
import InfoTooltip from '../../../ui/info-tooltip';
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display';
|
||||
|
||||
const EditGasItem = ({ estimateType, onClose }) => {
|
||||
import { useCustomTimeEstimate } from './useCustomTimeEstimate';
|
||||
|
||||
const EditGasItem = ({ priorityLevel, onClose }) => {
|
||||
const {
|
||||
estimateUsed,
|
||||
gasFeeEstimates,
|
||||
gasLimit,
|
||||
setEstimateToUse,
|
||||
updateTransaction,
|
||||
maxFeePerGas: maxFeePerGasValue,
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGasValue,
|
||||
updateTransactionUsingGasFeeEstimates,
|
||||
transaction: { dappSuggestedGasFees },
|
||||
} = useGasFeeContext();
|
||||
const t = useI18nContext();
|
||||
const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues);
|
||||
let maxFeePerGas;
|
||||
let maxPriorityFeePerGas;
|
||||
let minWaitTime;
|
||||
|
||||
const { minWaitTimeEstimate, suggestedMaxFeePerGas } =
|
||||
gasFeeEstimates[estimateType] || {};
|
||||
const hexMaximumTransactionFee = suggestedMaxFeePerGas
|
||||
if (gasFeeEstimates[priorityLevel]) {
|
||||
maxFeePerGas = gasFeeEstimates[priorityLevel].suggestedMaxFeePerGas;
|
||||
} else if (
|
||||
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED &&
|
||||
dappSuggestedGasFees
|
||||
) {
|
||||
maxFeePerGas = hexWEIToDecGWEI(dappSuggestedGasFees.maxFeePerGas);
|
||||
maxPriorityFeePerGas = hexWEIToDecGWEI(
|
||||
dappSuggestedGasFees.maxPriorityFeePerGas,
|
||||
);
|
||||
} else if (priorityLevel === PRIORITY_LEVELS.CUSTOM) {
|
||||
if (estimateUsed === PRIORITY_LEVELS.CUSTOM) {
|
||||
maxFeePerGas = maxFeePerGasValue;
|
||||
maxPriorityFeePerGas = maxPriorityFeePerGasValue;
|
||||
} else if (advancedGasFeeValues) {
|
||||
maxFeePerGas =
|
||||
gasFeeEstimates.estimatedBaseFee *
|
||||
parseFloat(advancedGasFeeValues.maxBaseFee);
|
||||
maxPriorityFeePerGas = advancedGasFeeValues.priorityFee;
|
||||
}
|
||||
}
|
||||
|
||||
const { waitTimeEstimate } = useCustomTimeEstimate({
|
||||
gasFeeEstimates,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
});
|
||||
|
||||
if (gasFeeEstimates[priorityLevel]) {
|
||||
minWaitTime =
|
||||
priorityLevel === PRIORITY_LEVELS.HIGH
|
||||
? gasFeeEstimates?.high.minWaitTimeEstimate
|
||||
: gasFeeEstimates?.low.maxWaitTimeEstimate;
|
||||
} else {
|
||||
minWaitTime = waitTimeEstimate;
|
||||
}
|
||||
|
||||
const hexMaximumTransactionFee = maxFeePerGas
|
||||
? getMaximumGasTotalInHexWei({
|
||||
gasLimit: decimalToHex(gasLimit),
|
||||
maxFeePerGas: decGWEIToHexWEI(suggestedMaxFeePerGas),
|
||||
maxFeePerGas: decGWEIToHexWEI(maxFeePerGas),
|
||||
})
|
||||
: null;
|
||||
|
||||
const onOptionSelect = () => {
|
||||
setEstimateToUse(estimateType);
|
||||
updateTransaction(estimateType);
|
||||
if (priorityLevel !== PRIORITY_LEVELS.CUSTOM) {
|
||||
updateTransactionUsingGasFeeEstimates(priorityLevel);
|
||||
}
|
||||
// todo: open advance modal if priorityLevel is custom
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('edit-gas-item', {
|
||||
'edit-gas-item--selected': estimateType === estimateUsed,
|
||||
'edit-gas-item-selected': priorityLevel === estimateUsed,
|
||||
'edit-gas-item-disabled':
|
||||
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED &&
|
||||
!dappSuggestedGasFees,
|
||||
})}
|
||||
role="button"
|
||||
onClick={onOptionSelect}
|
||||
>
|
||||
<span className="edit-gas-item__name">
|
||||
<span className="edit-gas-item__icon">
|
||||
{PRIORITY_LEVEL_ICON_MAP[estimateType]}
|
||||
<span
|
||||
className={`edit-gas-item__icon edit-gas-item__icon-${priorityLevel}`}
|
||||
>
|
||||
{PRIORITY_LEVEL_ICON_MAP[priorityLevel]}
|
||||
</span>
|
||||
<I18nValue messageKey={estimateType} />
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__time-estimate edit-gas-item__time-estimate-${estimateType}`}
|
||||
>
|
||||
{minWaitTimeEstimate && toHumanReadableTime(t, minWaitTimeEstimate)}
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__fee-estimate edit-gas-item__fee-estimate-${estimateType}`}
|
||||
>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
key="editGasSubTextFeeAmount"
|
||||
type={PRIMARY}
|
||||
value={hexMaximumTransactionFee}
|
||||
<I18nValue
|
||||
messageKey={
|
||||
priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED
|
||||
? 'dappSuggestedShortLabel'
|
||||
: priorityLevel
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__time-estimate edit-gas-item__time-estimate-${priorityLevel}`}
|
||||
>
|
||||
{minWaitTime
|
||||
? minWaitTime && toHumanReadableTime(t, minWaitTime)
|
||||
: '--'}
|
||||
</span>
|
||||
<span
|
||||
className={`edit-gas-item__fee-estimate edit-gas-item__fee-estimate-${priorityLevel}`}
|
||||
>
|
||||
{hexMaximumTransactionFee ? (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
key="editGasSubTextFeeAmount"
|
||||
type={PRIMARY}
|
||||
value={hexMaximumTransactionFee}
|
||||
/>
|
||||
) : (
|
||||
'--'
|
||||
)}
|
||||
</span>
|
||||
<span className="edit-gas-item__tooltip">
|
||||
<InfoTooltip position="top" />
|
||||
</span>
|
||||
@ -77,7 +143,7 @@ const EditGasItem = ({ estimateType, onClose }) => {
|
||||
};
|
||||
|
||||
EditGasItem.propTypes = {
|
||||
estimateType: PropTypes.string,
|
||||
priorityLevel: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,9 @@ jest.mock('../../../../store/actions', () => ({
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve('unknown')),
|
||||
}));
|
||||
|
||||
const MOCK_FEE_ESTIMATE = {
|
||||
@ -38,7 +41,12 @@ const MOCK_FEE_ESTIMATE = {
|
||||
estimatedBaseFee: '50',
|
||||
};
|
||||
|
||||
const renderComponent = (props) => {
|
||||
const DAPP_SUGGESTED_ESTIMATE = {
|
||||
maxFeePerGas: '0x59682f10',
|
||||
maxPriorityFeePerGas: '0x59682f00',
|
||||
};
|
||||
|
||||
const renderComponent = (props, transactionProps, gasFeeContextProps) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
@ -53,41 +61,78 @@ const renderComponent = (props) => {
|
||||
selectedAddress: '0xAddress',
|
||||
featureFlags: { advancedInlineGas: true },
|
||||
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
||||
advancedGasFee: {
|
||||
maxBaseFee: '1.5',
|
||||
priorityFee: '2',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider transaction={{ txParams: { gas: '0x5208' } }}>
|
||||
<EditGasItem estimateType="low" {...props} />
|
||||
<GasFeeContextProvider
|
||||
transaction={{ txParams: { gas: '0x5208' }, ...transactionProps }}
|
||||
{...gasFeeContextProps}
|
||||
>
|
||||
<EditGasItem priorityLevel="low" {...props} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('EditGasItem', () => {
|
||||
it('should renders low gas estimate options for estimateType low', () => {
|
||||
renderComponent({ estimateType: 'low' });
|
||||
|
||||
it('should renders low gas estimate option for priorityLevel low', () => {
|
||||
renderComponent({ priorityLevel: 'low' });
|
||||
expect(screen.queryByText('🐢')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Low')).toBeInTheDocument();
|
||||
expect(screen.queryByText('6 min')).toBeInTheDocument();
|
||||
expect(screen.queryByText('5 min')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('0.001113 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should renders market gas estimate options for estimateType medium', () => {
|
||||
renderComponent({ estimateType: 'medium' });
|
||||
|
||||
it('should renders market gas estimate option for priorityLevel medium', () => {
|
||||
renderComponent({ priorityLevel: 'medium' });
|
||||
expect(screen.queryByText('🦊')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Market')).toBeInTheDocument();
|
||||
expect(screen.queryByText('30 sec')).toBeInTheDocument();
|
||||
expect(screen.queryByText('5 min')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('0.00147 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should renders aggressive gas estimate options for estimateType high', () => {
|
||||
renderComponent({ estimateType: 'high' });
|
||||
|
||||
it('should renders aggressive gas estimate option for priorityLevel high', () => {
|
||||
renderComponent({ priorityLevel: 'high' });
|
||||
expect(screen.queryByText('🦍')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Aggressive')).toBeInTheDocument();
|
||||
expect(screen.queryByText('15 sec')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('0.0021 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should highlight option is priorityLevel is currently selected', () => {
|
||||
renderComponent({ priorityLevel: 'high' }, { userFeeLevel: 'high' });
|
||||
expect(
|
||||
document.getElementsByClassName('edit-gas-item-selected'),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should renders site gas estimate option for priorityLevel dappSuggested', () => {
|
||||
renderComponent(
|
||||
{ priorityLevel: 'dappSuggested' },
|
||||
{ dappSuggestedGasFees: DAPP_SUGGESTED_ESTIMATE },
|
||||
);
|
||||
expect(screen.queryByText('🌐')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Site')).toBeInTheDocument();
|
||||
expect(screen.queryByTitle('0.0000315 ETH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should disable site gas estimate option for is transaction does not have dappSuggestedGasFees', async () => {
|
||||
renderComponent({ priorityLevel: 'dappSuggested' });
|
||||
expect(
|
||||
document.getElementsByClassName('edit-gas-item-disabled'),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should renders advance gas estimate option for priorityLevel custom', () => {
|
||||
renderComponent({ priorityLevel: 'custom' });
|
||||
expect(screen.queryByText('⚙')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Advanced')).toBeInTheDocument();
|
||||
// below value of custom gas fee estimate is default obtained from state.metamask.advancedGasFee
|
||||
expect(screen.queryByTitle('0.001575 ETH')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,8 @@
|
||||
color: $ui-4;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 12px 0;
|
||||
padding: 4px 12px;
|
||||
height: 32px;
|
||||
@ -11,8 +13,13 @@
|
||||
background-color: $ui-1;
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: $ui-black;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
@ -21,6 +28,11 @@
|
||||
|
||||
&__icon {
|
||||
margin-right: 4px;
|
||||
|
||||
&-custom {
|
||||
font-size: 20px;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__time-estimate {
|
||||
|
@ -0,0 +1,83 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { GAS_ESTIMATE_TYPES } from '../../../../../shared/constants/gas';
|
||||
import {
|
||||
getGasEstimateType,
|
||||
getIsGasEstimatesLoading,
|
||||
} from '../../../../ducks/metamask/metamask';
|
||||
import { getGasFeeTimeEstimate } from '../../../../store/actions';
|
||||
|
||||
export const useCustomTimeEstimate = ({
|
||||
gasFeeEstimates,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
}) => {
|
||||
const gasEstimateType = useSelector(getGasEstimateType);
|
||||
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||
|
||||
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
||||
|
||||
const returnNoEstimates =
|
||||
isGasEstimatesLoading ||
|
||||
gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET ||
|
||||
!maxPriorityFeePerGas;
|
||||
|
||||
// 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
|
||||
// the time to show
|
||||
const isUnknownLow =
|
||||
gasFeeEstimates?.low &&
|
||||
Number(maxPriorityFeePerGas) <
|
||||
Number(gasFeeEstimates.low.suggestedMaxPriorityFeePerGas);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isGasEstimatesLoading ||
|
||||
gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET ||
|
||||
!maxPriorityFeePerGas
|
||||
)
|
||||
return;
|
||||
if (isUnknownLow) {
|
||||
// getGasFeeTimeEstimate requires parameters in string format
|
||||
getGasFeeTimeEstimate(
|
||||
new BigNumber(maxPriorityFeePerGas, 10).toString(10),
|
||||
new BigNumber(maxFeePerGas, 10).toString(10),
|
||||
).then((result) => {
|
||||
setCustomEstimatedTime(result);
|
||||
});
|
||||
}
|
||||
}, [
|
||||
gasEstimateType,
|
||||
isUnknownLow,
|
||||
isGasEstimatesLoading,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
returnNoEstimates,
|
||||
]);
|
||||
|
||||
if (returnNoEstimates) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { low = {}, medium = {}, high = {} } = gasFeeEstimates;
|
||||
let waitTimeEstimate = '';
|
||||
|
||||
if (
|
||||
isUnknownLow &&
|
||||
customEstimatedTime &&
|
||||
customEstimatedTime !== 'unknown' &&
|
||||
customEstimatedTime?.upperTimeBound !== 'unknown'
|
||||
) {
|
||||
waitTimeEstimate = Number(customEstimatedTime?.upperTimeBound);
|
||||
} else if (
|
||||
Number(maxPriorityFeePerGas) >= Number(medium.suggestedMaxPriorityFeePerGas)
|
||||
) {
|
||||
waitTimeEstimate = high.minWaitTimeEstimate;
|
||||
} else {
|
||||
waitTimeEstimate = low.maxWaitTimeEstimate;
|
||||
}
|
||||
|
||||
return { waitTimeEstimate };
|
||||
};
|
@ -31,5 +31,10 @@
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
&__separator {
|
||||
border-top: 1px solid $ui-grey;
|
||||
margin: 8px 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,10 @@ export default function TransactionDetail({ rows = [], onEdit }) {
|
||||
const t = useI18nContext();
|
||||
const {
|
||||
gasLimit,
|
||||
gasPrice,
|
||||
estimateUsed,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
transaction,
|
||||
supportsEIP1559,
|
||||
} = useGasFeeContext();
|
||||
|
||||
if (EIP_1559_V2 && estimateUsed) {
|
||||
@ -48,23 +46,14 @@ export default function TransactionDetail({ rows = [], onEdit }) {
|
||||
<Typography fontSize="12px" color={COLORS.GREY}>
|
||||
{t('dappSuggestedTooltip', [transaction.origin])}
|
||||
</Typography>
|
||||
{supportsEIP1559 ? (
|
||||
<>
|
||||
<Typography fontSize="12px">
|
||||
<b>{t('maxBaseFee')}</b>
|
||||
{maxFeePerGas}
|
||||
</Typography>
|
||||
<Typography fontSize="12px">
|
||||
<b>{t('maxPriorityFee')}</b>
|
||||
{maxPriorityFeePerGas}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Typography fontSize="12px">
|
||||
<b>{t('gasPriceLabel')}</b>
|
||||
{gasPrice}
|
||||
</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}
|
||||
|
@ -31,7 +31,7 @@ export function useGasFeeContext() {
|
||||
GasFeeContextProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
defaultEstimateToUse: PropTypes.string,
|
||||
transaction: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.object,
|
||||
minimumGasLimit: PropTypes.string,
|
||||
editGasMode: PropTypes.string,
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import {
|
||||
@ -108,6 +108,19 @@ export function useGasFeeInputs(
|
||||
return estimateToUse;
|
||||
});
|
||||
|
||||
/**
|
||||
* In EIP-1559 V2 designs change to gas estimate is always updated to transaction
|
||||
* Thus callback setEstimateToUse can be deprecate in favour of this useEffect
|
||||
* so that transaction is source of truth whenever possible.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (areDappSuggestedAndTxParamGasFeesTheSame(transaction)) {
|
||||
setEstimateUsed('dappSuggested');
|
||||
} else if (transaction?.userFeeLevel) {
|
||||
setEstimateUsed(transaction?.userFeeLevel);
|
||||
}
|
||||
}, [setEstimateUsed, transaction]);
|
||||
|
||||
const [gasLimit, setGasLimit] = useState(() =>
|
||||
Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')),
|
||||
);
|
||||
@ -198,7 +211,7 @@ export function useGasFeeInputs(
|
||||
}
|
||||
}, [minimumGasLimit, gasErrors.gasLimit, transaction]);
|
||||
|
||||
const { updateTransaction } = useTransactionFunctions({
|
||||
const { updateTransactionUsingGasFeeEstimates } = useTransactionFunctions({
|
||||
defaultEstimateToUse,
|
||||
gasLimit,
|
||||
gasPrice,
|
||||
@ -289,6 +302,6 @@ export function useGasFeeInputs(
|
||||
gasWarnings,
|
||||
hasGasErrors,
|
||||
supportsEIP1559,
|
||||
updateTransaction,
|
||||
updateTransactionUsingGasFeeEstimates,
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { PRIORITY_LEVELS } from '../../../shared/constants/gas';
|
||||
import {
|
||||
decGWEIToHexWEI,
|
||||
decimalToHex,
|
||||
@ -16,24 +17,19 @@ export const useTransactionFunctions = ({
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const updateTransaction = useCallback(
|
||||
(estimateType) => {
|
||||
(estimateUsed, maxFeePerGas, maxPriorityFeePerGas) => {
|
||||
const newGasSettings = {
|
||||
gas: decimalToHex(gasLimit),
|
||||
gasLimit: decimalToHex(gasLimit),
|
||||
estimateSuggested: defaultEstimateToUse,
|
||||
estimateUsed: estimateType,
|
||||
estimateUsed,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
};
|
||||
|
||||
newGasSettings.maxFeePerGas = decGWEIToHexWEI(
|
||||
gasFeeEstimates[estimateType].suggestedMaxFeePerGas,
|
||||
);
|
||||
newGasSettings.maxPriorityFeePerGas = decGWEIToHexWEI(
|
||||
gasFeeEstimates[estimateType].suggestedMaxPriorityFeePerGas,
|
||||
);
|
||||
|
||||
const updatedTxMeta = {
|
||||
...transaction,
|
||||
userFeeLevel: estimateType || 'custom',
|
||||
userFeeLevel: estimateUsed || 'custom',
|
||||
txParams: {
|
||||
...transaction.txParams,
|
||||
...newGasSettings,
|
||||
@ -42,8 +38,35 @@ export const useTransactionFunctions = ({
|
||||
|
||||
dispatch(updateTransactionFn(updatedTxMeta));
|
||||
},
|
||||
[defaultEstimateToUse, dispatch, gasLimit, gasFeeEstimates, transaction],
|
||||
[defaultEstimateToUse, dispatch, gasLimit, transaction],
|
||||
);
|
||||
|
||||
return { updateTransaction };
|
||||
const updateTransactionUsingGasFeeEstimates = useCallback(
|
||||
(gasFeeEstimateToUse) => {
|
||||
if (gasFeeEstimateToUse === PRIORITY_LEVELS.DAPP_SUGGESTED) {
|
||||
const {
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
} = transaction?.dappSuggestedGasFees;
|
||||
updateTransaction(
|
||||
PRIORITY_LEVELS.CUSTOM,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
);
|
||||
} else {
|
||||
const {
|
||||
suggestedMaxFeePerGas,
|
||||
suggestedMaxPriorityFeePerGas,
|
||||
} = gasFeeEstimates[gasFeeEstimateToUse];
|
||||
updateTransaction(
|
||||
gasFeeEstimateToUse,
|
||||
decGWEIToHexWEI(suggestedMaxFeePerGas),
|
||||
decGWEIToHexWEI(suggestedMaxPriorityFeePerGas),
|
||||
);
|
||||
}
|
||||
},
|
||||
[gasFeeEstimates, transaction?.dappSuggestedGasFees, updateTransaction],
|
||||
);
|
||||
|
||||
return { updateTransactionUsingGasFeeEstimates };
|
||||
};
|
||||
|
@ -26,7 +26,6 @@ const GasDetailsItem = ({
|
||||
isMainnet,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
supportsEIP1559,
|
||||
txData,
|
||||
useNativeCurrencyAsPrimaryCurrency,
|
||||
}) => {
|
||||
@ -120,16 +119,14 @@ const GasDetailsItem = ({
|
||||
</Box>,
|
||||
])}
|
||||
subTitle={
|
||||
supportsEIP1559 && (
|
||||
<GasTiming
|
||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||
maxPriorityFeePerGas || txData.txParams.maxPriorityFeePerGas,
|
||||
)}
|
||||
maxFeePerGas={hexWEIToDecGWEI(
|
||||
maxFeePerGas || txData.txParams.maxFeePerGas,
|
||||
)}
|
||||
/>
|
||||
)
|
||||
<GasTiming
|
||||
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||
maxPriorityFeePerGas || txData.txParams.maxPriorityFeePerGas,
|
||||
)}
|
||||
maxFeePerGas={hexWEIToDecGWEI(
|
||||
maxFeePerGas || txData.txParams.maxFeePerGas,
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
@ -141,7 +138,6 @@ GasDetailsItem.propTypes = {
|
||||
isMainnet: PropTypes.bool,
|
||||
maxFeePerGas: PropTypes.string,
|
||||
maxPriorityFeePerGas: PropTypes.string,
|
||||
supportsEIP1559: PropTypes.bool,
|
||||
txData: PropTypes.object,
|
||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
|
||||
import { ETH } from '../../../helpers/constants/common';
|
||||
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||
@ -14,6 +14,7 @@ jest.mock('../../../store/actions', () => ({
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
const render = (props) => {
|
||||
@ -37,28 +38,34 @@ const render = (props) => {
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider {...props}>
|
||||
<GasDetailsItem txData={{}} {...props} />
|
||||
<GasDetailsItem txData={{ txParams: {} }} {...props} />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('GasDetailsItem', () => {
|
||||
it('should render label', () => {
|
||||
it('should render label', async () => {
|
||||
render();
|
||||
expect(screen.queryByText('Gas')).toBeInTheDocument();
|
||||
expect(screen.queryByText('(estimated)')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||
expect(screen.queryByText('ETH')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Gas')).toBeInTheDocument();
|
||||
expect(screen.queryByText('(estimated)')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||
expect(screen.queryByText('ETH')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show warning icon if estimates are high', () => {
|
||||
it('should show warning icon if estimates are high', async () => {
|
||||
render({ defaultEstimateToUse: 'high' });
|
||||
expect(screen.queryByText('⚠ Max fee:')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('⚠ Max fee:')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show warning icon if estimates are not high', () => {
|
||||
it('should not show warning icon if estimates are not high', async () => {
|
||||
render({ defaultEstimateToUse: 'low' });
|
||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user