EIP-1559 - Improve gas timing logic to show more accurate verbiage (#11668)

feature/default_network_editable
David Walsh 3 years ago committed by GitHub
parent 714170c7b8
commit e283c03c4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      app/scripts/metamask-controller.js
  2. 26
      ui/components/app/edit-gas-display/edit-gas-display.component.js
  3. 8
      ui/components/app/edit-gas-display/index.scss
  4. 1
      ui/components/app/edit-gas-popover/edit-gas-popover.component.js
  5. 118
      ui/components/app/gas-timing/gas-timing.component.js
  6. 4
      ui/components/app/gas-timing/index.scss
  7. 1
      ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
  8. 7
      ui/store/actions.js

@ -1122,6 +1122,11 @@ export default class MetamaskController extends EventEmitter {
this.gasFeeController.disconnectPoller,
this.gasFeeController,
),
getGasFeeTimeEstimate: nodeify(
this.gasFeeController.getTimeEstimate,
this.gasFeeController,
),
};
}

@ -15,7 +15,6 @@ import {
COLORS,
TYPOGRAPHY,
FONT_WEIGHT,
TEXT_ALIGN,
} from '../../../helpers/constants/design-system';
import { areDappSuggestedAndTxParamGasFeesTheSame } from '../../../helpers/utils/confirm-tx.util';
@ -52,7 +51,6 @@ export default function EditGasDisplay({
setEstimateToUse,
estimatedMinimumFiat,
estimatedMaximumFiat,
hasGasErrors,
dappSuggestedGasFeeAcknowledged,
setDappSuggestedGasFeeAcknowledged,
showAdvancedForm,
@ -134,7 +132,12 @@ export default function EditGasDisplay({
</Typography>,
])
}
timing={<GasTiming maxPriorityFeePerGas={maxPriorityFeePerGas} />}
timing={
<GasTiming
maxFeePerGas={maxFeePerGas}
maxPriorityFeePerGas={maxPriorityFeePerGas}
/>
}
/>
{requireDappAcknowledgement && (
<Button
@ -144,22 +147,6 @@ export default function EditGasDisplay({
{t('gasDisplayAcknowledgeDappButtonText')}
</Button>
)}
{hasGasErrors && (
<div className="edit-gas-display__error">
<Typography
color={COLORS.ERROR1}
variant={TYPOGRAPHY.H7}
align={TEXT_ALIGN.CENTER}
fontWeight={FONT_WEIGHT.BOLD}
>
{t('editGasTooLow')}{' '}
<InfoTooltip
position="top"
contentText={t('editGasTooLowTooltip')}
/>
</Typography>
</div>
)}
{networkSupports1559 &&
!requireDappAcknowledgement &&
![EDIT_GAS_MODES.SPEED_UP, EDIT_GAS_MODES.CANCEL].includes(mode) && (
@ -259,7 +246,6 @@ EditGasDisplay.propTypes = {
setEstimateToUse: PropTypes.func,
estimatedMinimumFiat: PropTypes.string,
estimatedMaximumFiat: PropTypes.string,
hasGasErrors: PropTypes.boolean,
dappSuggestedGasFeeAcknowledged: PropTypes.boolean,
setDappSuggestedGasFeeAcknowledged: PropTypes.func,
showAdvancedForm: PropTypes.bool,

@ -21,14 +21,6 @@
}
}
&__error .info-tooltip {
display: inline-block;
path {
fill: $error-1;
}
}
&__dapp-acknowledgement-warning {
margin-bottom: 20px;
}

@ -235,7 +235,6 @@ export default function EditGasPopover({
onEducationClick={() => setShowEducationContent(true)}
mode={mode}
transaction={transaction}
hasGasErrors={hasGasErrors}
gasErrors={gasErrors}
onManualChange={onManualChange}
minimumGasLimit={minimumGasLimitDec}

@ -1,35 +1,86 @@
import React, { useContext } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
import { useGasFeeEstimates } from '../../../hooks/useGasFeeEstimates';
import { usePrevious } from '../../../hooks/usePrevious';
import { I18nContext } from '../../../contexts/i18n';
import Typography from '../../ui/typography/typography';
import { TYPOGRAPHY } from '../../../helpers/constants/design-system';
import {
TYPOGRAPHY,
FONT_WEIGHT,
} from '../../../helpers/constants/design-system';
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
import { getGasTimeEstimate } from '../../../store/actions';
// Once we reach this second threshold, we switch to minutes as a unit
const SECOND_CUTOFF = 90;
export default function GasTiming({ maxPriorityFeePerGas }) {
// Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes"
const toHumanReadableTime = (milliseconds = 1, t) => {
const seconds = Math.ceil(milliseconds / 1000);
if (seconds <= SECOND_CUTOFF) {
return t('gasTimingSeconds', [seconds]);
}
return t('gasTimingMinutes', [Math.ceil(seconds / 60)]);
};
export default function GasTiming({
maxFeePerGas = 0,
maxPriorityFeePerGas = 0,
}) {
const {
gasFeeEstimates,
isGasEstimatesLoading,
gasEstimateType,
} = useGasFeeEstimates();
const t = useContext(I18nContext);
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
// Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes"
const toHumanReadableTime = (milliseconds = 1) => {
const seconds = Math.ceil(milliseconds / 1000);
if (seconds <= SECOND_CUTOFF) {
return t('gasTimingSeconds', [seconds]);
// 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);
const previousMaxFeePerGas = usePrevious(maxFeePerGas);
const previousMaxPriorityFeePerGas = usePrevious(maxPriorityFeePerGas);
const previousIsUnknownLow = usePrevious(isUnknownLow);
useEffect(() => {
const priority = maxPriorityFeePerGas;
const fee = maxFeePerGas;
if (
isUnknownLow ||
priority !== previousMaxPriorityFeePerGas ||
fee !== previousMaxFeePerGas
) {
getGasTimeEstimate(priority, fee).then((result) => {
if (maxFeePerGas === fee && maxPriorityFeePerGas === priority) {
setCustomEstimatedTime(result);
}
});
}
return t('gasTimingMinutes', [Math.ceil(seconds / 60)]);
};
if (isUnknownLow !== false && previousIsUnknownLow === true) {
setCustomEstimatedTime(null);
}
}, [
maxPriorityFeePerGas,
maxFeePerGas,
isUnknownLow,
previousMaxFeePerGas,
previousMaxPriorityFeePerGas,
previousIsUnknownLow,
]);
const t = useContext(I18nContext);
// Don't show anything if we don't have enough information
if (
@ -39,39 +90,69 @@ export default function GasTiming({ maxPriorityFeePerGas }) {
return null;
}
const { low, medium, high } = gasFeeEstimates;
const { low = {}, medium = {}, high = {} } = gasFeeEstimates;
let text = '';
let attitude = '';
let attitude = 'positive';
let fontWeight = FONT_WEIGHT.NORMAL;
// Anything medium or faster is positive
if (
Number(maxPriorityFeePerGas) >= Number(medium.suggestedMaxPriorityFeePerGas)
) {
attitude = 'positive';
// High+ is very likely, medium is likely
if (
Number(maxPriorityFeePerGas) < Number(high.suggestedMaxPriorityFeePerGas)
) {
// Medium
text = t('gasTimingPositive', [
toHumanReadableTime(medium.maxWaitTimeEstimate),
toHumanReadableTime(low.maxWaitTimeEstimate, t),
]);
} else {
// High
text = t('gasTimingVeryPositive', [
toHumanReadableTime(high.maxWaitTimeEstimate),
toHumanReadableTime(high.minWaitTimeEstimate, t),
]);
}
} else {
attitude = 'negative';
// If the user has chosen a value less than our low estimate,
// calculate a potential wait time
if (isUnknownLow) {
// If we didn't get any useful information, show the
// "unknown processing time" message
if (
!customEstimatedTime ||
customEstimatedTime === 'unknown' ||
customEstimatedTime?.upperTimeBound === 'unknown'
) {
fontWeight = FONT_WEIGHT.BOLD;
text = (
<>
{t('editGasTooLow')}{' '}
<InfoTooltip
position="top"
contentText={t('editGasTooLowTooltip')}
/>
</>
);
} else {
text = t('gasTimingNegative', [
toHumanReadableTime(Number(customEstimatedTime?.upperTimeBound), t),
]);
}
} else {
text = t('gasTimingNegative', [
toHumanReadableTime(low.maxWaitTimeEstimate),
toHumanReadableTime(low.maxWaitTimeEstimate, t),
]);
}
}
return (
<Typography
variant={TYPOGRAPHY.H7}
fontWeight={fontWeight}
className={classNames('gas-timing', {
[`gas-timing--${attitude}`]: attitude,
})}
@ -83,4 +164,5 @@ export default function GasTiming({ maxPriorityFeePerGas }) {
GasTiming.propTypes = {
maxPriorityFeePerGas: PropTypes.string.isRequired,
maxFeePerGas: PropTypes.string.isRequired,
};

@ -16,5 +16,9 @@
.info-tooltip {
display: inline-block;
margin-inline-start: 4px;
path {
fill: $error-1;
}
}
}

@ -447,6 +447,7 @@ export default class ConfirmTransactionBase extends Component {
subTitle={
<GasTiming
maxPriorityFeePerGas={txData.txParams.maxPriorityFeePerGas}
maxFeePerGas={txData.txParams.maxFeePerGas}
/>
}
/>,

@ -2791,6 +2791,13 @@ export function disconnectGasFeeEstimatePoller(pollToken) {
return promisifiedBackground.disconnectGasFeeEstimatePoller(pollToken);
}
export function getGasTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) {
return promisifiedBackground.getGasTimeEstimate(
maxPriorityFeePerGas,
maxFeePerGas,
);
}
// MetaMetrics
/**
* @typedef {import('../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload

Loading…
Cancel
Save