mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Provide Estimate Data to EditGasDisplay (#11433)
This commit is contained in:
parent
c3e6514c35
commit
1da7beed13
@ -650,6 +650,12 @@
|
||||
"editGasLow": {
|
||||
"message": "Low"
|
||||
},
|
||||
"editGasMaxFeeLow": {
|
||||
"message": "Max fee too low for network conditions"
|
||||
},
|
||||
"editGasMaxPriorityFeeLow": {
|
||||
"message": "Max priority fee too low for network conditions"
|
||||
},
|
||||
"editGasMedium": {
|
||||
"message": "Medium"
|
||||
},
|
||||
@ -659,6 +665,13 @@
|
||||
"editGasTitle": {
|
||||
"message": "Edit gas fee"
|
||||
},
|
||||
"editGasTooLow": {
|
||||
"message": "Unknown processing time"
|
||||
},
|
||||
"editGasTotalBannerSubtitle": {
|
||||
"message": "Up to $1 ($2)",
|
||||
"display": "$1 represents a fiat value"
|
||||
},
|
||||
"editNonceField": {
|
||||
"message": "Edit Nonce"
|
||||
},
|
||||
|
@ -20,3 +20,12 @@ export const GAS_ESTIMATE_TYPES = {
|
||||
ETH_GASPRICE: 'eth_gasPrice',
|
||||
NONE: 'none',
|
||||
};
|
||||
|
||||
/**
|
||||
* These represent gas recommendation levels presented in the UI
|
||||
*/
|
||||
export const GAS_RECOMMENDATIONS = {
|
||||
LOW: 'low',
|
||||
MEDIUM: 'medium',
|
||||
HIGH: 'high',
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import Typography from '../../ui/typography/typography';
|
||||
@ -8,16 +9,55 @@ import {
|
||||
COLORS,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import FormField from '../../ui/form-field';
|
||||
import {
|
||||
GAS_ESTIMATE_TYPES,
|
||||
GAS_RECOMMENDATIONS,
|
||||
} from '../../../../shared/constants/gas';
|
||||
|
||||
export default function AdvancedGasControls() {
|
||||
const DEFAULT_ESTIMATES_LEVEL = 'medium';
|
||||
|
||||
export default function AdvancedGasControls({
|
||||
estimateToUse,
|
||||
gasFeeEstimates,
|
||||
gasEstimateType,
|
||||
maxPriorityFee,
|
||||
maxFee,
|
||||
setMaxPriorityFee,
|
||||
setMaxFee,
|
||||
onManualChange,
|
||||
gasLimit,
|
||||
setGasLimit,
|
||||
gasPrice,
|
||||
setGasPrice,
|
||||
maxPriorityFeeFiat,
|
||||
maxFeeFiat,
|
||||
maxPriorityFeeError,
|
||||
maxFeeError,
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
const [gasLimit, setGasLimit] = useState(0);
|
||||
const [maxPriorityFee, setMaxPriorityFee] = useState(0);
|
||||
const [maxFee, setMaxFee] = useState(0);
|
||||
const suggestedValues = {};
|
||||
|
||||
// Used in legacy version
|
||||
const [gasPrice, setGasPrice] = useState(0);
|
||||
switch (gasEstimateType) {
|
||||
case GAS_ESTIMATE_TYPES.FEE_MARKET:
|
||||
suggestedValues.maxPriorityFeePerGas =
|
||||
gasFeeEstimates?.[estimateToUse]?.suggestedMaxPriorityFeePerGas;
|
||||
suggestedValues.maxFeePerGas =
|
||||
gasFeeEstimates?.[estimateToUse]?.suggestedMaxFeePerGas;
|
||||
break;
|
||||
case GAS_ESTIMATE_TYPES.LEGACY:
|
||||
suggestedValues.gasPrice = gasFeeEstimates?.[estimateToUse];
|
||||
break;
|
||||
case GAS_ESTIMATE_TYPES.ETH_GASPRICE:
|
||||
suggestedValues.gasPrice = gasFeeEstimates?.gasPrice;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const showFeeMarketFields =
|
||||
process.env.SHOW_EIP_1559_UI &&
|
||||
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET;
|
||||
|
||||
return (
|
||||
<div className="advanced-gas-controls">
|
||||
@ -27,58 +67,83 @@ export default function AdvancedGasControls() {
|
||||
tooltipText=""
|
||||
value={gasLimit}
|
||||
numeric
|
||||
autoFocus
|
||||
/>
|
||||
{process.env.SHOW_EIP_1559_UI ? (
|
||||
{showFeeMarketFields ? (
|
||||
<>
|
||||
<FormField
|
||||
titleText={t('maxPriorityFee')}
|
||||
titleUnit="(GWEI)"
|
||||
tooltipText=""
|
||||
onChange={setMaxPriorityFee}
|
||||
onChange={(value) => {
|
||||
setMaxPriorityFee(value);
|
||||
onManualChange?.();
|
||||
}}
|
||||
value={maxPriorityFee}
|
||||
detailText={maxPriorityFeeFiat}
|
||||
numeric
|
||||
titleDetail={
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
></Typography>
|
||||
</>
|
||||
suggestedValues.maxPriorityFeePerGas && (
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
>
|
||||
{
|
||||
gasFeeEstimates?.[DEFAULT_ESTIMATES_LEVEL]
|
||||
?.suggestedMaxPriorityFeePerGas
|
||||
}
|
||||
</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
error={maxPriorityFeeError}
|
||||
/>
|
||||
<FormField
|
||||
titleText={t('maxFee')}
|
||||
titleUnit="(GWEI)"
|
||||
tooltipText=""
|
||||
onChange={setMaxFee}
|
||||
onChange={(value) => {
|
||||
setMaxFee(value);
|
||||
onManualChange?.();
|
||||
}}
|
||||
value={maxFee}
|
||||
numeric
|
||||
detailText={maxFeeFiat}
|
||||
titleDetail={
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
></Typography>
|
||||
</>
|
||||
suggestedValues.maxFeePerGas && (
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
>
|
||||
{
|
||||
gasFeeEstimates?.[DEFAULT_ESTIMATES_LEVEL]
|
||||
?.suggestedMaxFeePerGas
|
||||
}
|
||||
</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
error={maxFeeError}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@ -86,13 +151,71 @@ export default function AdvancedGasControls() {
|
||||
<FormField
|
||||
titleText={t('gasPrice')}
|
||||
titleUnit="(GWEI)"
|
||||
onChange={setGasPrice}
|
||||
onChange={(value) => {
|
||||
setGasPrice(value);
|
||||
onManualChange?.();
|
||||
}}
|
||||
tooltipText={t('editGasPriceTooltip')}
|
||||
value={gasPrice}
|
||||
numeric
|
||||
titleDetail={
|
||||
suggestedValues.gasPrice && (
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
>
|
||||
{suggestedValues.gasPrice}
|
||||
</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
AdvancedGasControls.propTypes = {
|
||||
estimateToUse: PropTypes.oneOf(Object.values(GAS_RECOMMENDATIONS)),
|
||||
gasFeeEstimates: PropTypes.oneOf([
|
||||
PropTypes.shape({
|
||||
gasPrice: PropTypes.string,
|
||||
}),
|
||||
PropTypes.shape({
|
||||
low: PropTypes.string,
|
||||
medium: PropTypes.string,
|
||||
high: PropTypes.string,
|
||||
}),
|
||||
PropTypes.shape({
|
||||
low: PropTypes.object,
|
||||
medium: PropTypes.object,
|
||||
high: PropTypes.object,
|
||||
estimatedBaseFee: PropTypes.string,
|
||||
}),
|
||||
]),
|
||||
gasEstimateType: PropTypes.oneOf(Object.values(GAS_ESTIMATE_TYPES)),
|
||||
setMaxPriorityFee: PropTypes.func,
|
||||
setMaxFee: PropTypes.func,
|
||||
maxPriorityFee: PropTypes.number,
|
||||
maxFee: PropTypes.number,
|
||||
onManualChange: PropTypes.func,
|
||||
gasLimit: PropTypes.number,
|
||||
setGasLimit: PropTypes.func,
|
||||
gasPrice: PropTypes.number,
|
||||
setGasPrice: PropTypes.func,
|
||||
maxPriorityFeeFiat: PropTypes.string,
|
||||
maxFeeFiat: PropTypes.string,
|
||||
maxPriorityFeeError: PropTypes.string,
|
||||
maxFeeError: PropTypes.string,
|
||||
};
|
||||
|
@ -1,21 +1,25 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { GAS_RECOMMENDATIONS } from '../../../../shared/constants/gas';
|
||||
|
||||
import Button from '../../ui/button';
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import {
|
||||
COLORS,
|
||||
TYPOGRAPHY,
|
||||
FONT_WEIGHT,
|
||||
TEXT_ALIGN,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
import InfoTooltip from '../../ui/info-tooltip';
|
||||
import TransactionTotalBanner from '../transaction-total-banner/transaction-total-banner.component';
|
||||
import RadioGroup from '../../ui/radio-group/radio-group.component';
|
||||
import AdvancedGasControls from '../advanced-gas-controls/advanced-gas-controls.component';
|
||||
import ActionableMessage from '../../ui/actionable-message/actionable-message';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import ActionableMessage from '../../ui/actionable-message/actionable-message';
|
||||
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
||||
|
||||
export default function EditGasDisplay({
|
||||
alwaysShowForm,
|
||||
@ -24,18 +28,45 @@ export default function EditGasDisplay({
|
||||
onEducationClick,
|
||||
dappSuggestedGasFee,
|
||||
dappOrigin,
|
||||
defaultEstimateToUse = 'medium',
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
const [warning] = useState(null);
|
||||
|
||||
const [showAdvancedForm, setShowAdvancedForm] = useState(false);
|
||||
const [
|
||||
dappSuggestedGasFeeAcknowledged,
|
||||
setDappSuggestedGasFeeAcknowledged,
|
||||
] = useState(false);
|
||||
|
||||
const requireDappAcknowledgement =
|
||||
dappSuggestedGasFee && !dappSuggestedGasFeeAcknowledged;
|
||||
const requireDappAcknowledgement = Boolean(
|
||||
dappSuggestedGasFee && !dappSuggestedGasFeeAcknowledged,
|
||||
);
|
||||
|
||||
const {
|
||||
maxPriorityFeePerGas,
|
||||
setMaxPriorityFeePerGas,
|
||||
maxPriorityFeePerGasFiat,
|
||||
maxFeePerGas,
|
||||
setMaxFeePerGas,
|
||||
maxFeePerGasFiat,
|
||||
estimatedMaximumNative,
|
||||
isGasEstimatesLoading,
|
||||
gasFeeEstimates,
|
||||
gasEstimateType,
|
||||
gasPrice,
|
||||
setGasPrice,
|
||||
gasLimit,
|
||||
setGasLimit,
|
||||
estimateToUse,
|
||||
setEstimateToUse,
|
||||
estimatedMinimumFiat,
|
||||
estimatedMaximumFiat,
|
||||
isMaxFeeError,
|
||||
isMaxPriorityFeeError,
|
||||
isGasTooLow,
|
||||
} = useGasFeeInputs(defaultEstimateToUse);
|
||||
|
||||
return (
|
||||
<div className="edit-gas-display">
|
||||
@ -44,7 +75,7 @@ export default function EditGasDisplay({
|
||||
<div className="edit-gas-display__warning">
|
||||
<ActionableMessage
|
||||
className="actionable-message--warning"
|
||||
message="Swaps are time sensitive. “Medium” is not reccomended."
|
||||
message={warning}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -72,9 +103,25 @@ export default function EditGasDisplay({
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TransactionTotalBanner total="" detail="" timing="" />
|
||||
|
||||
<TransactionTotalBanner
|
||||
total={estimatedMinimumFiat}
|
||||
detail={
|
||||
process.env.SHOW_EIP_1559_UI &&
|
||||
t('editGasTotalBannerSubtitle', [
|
||||
<Typography
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
tag="span"
|
||||
key="secondary"
|
||||
>
|
||||
{estimatedMaximumFiat}
|
||||
</Typography>,
|
||||
<Typography tag="span" key="primary">
|
||||
{estimatedMaximumNative}
|
||||
</Typography>,
|
||||
])
|
||||
}
|
||||
timing=""
|
||||
/>
|
||||
{requireDappAcknowledgement && (
|
||||
<Button
|
||||
className="edit-gas-display__dapp-acknowledgement-button"
|
||||
@ -83,23 +130,43 @@ export default function EditGasDisplay({
|
||||
{t('gasDisplayAcknowledgeDappButtonText')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{isGasTooLow && (
|
||||
<div className="edit-gas-display__error">
|
||||
<Typography
|
||||
color={COLORS.ERROR1}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
align={TEXT_ALIGN.CENTER}
|
||||
>
|
||||
{t('editGasTooLow')}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
{!requireDappAcknowledgement && (
|
||||
<RadioGroup
|
||||
name="gas-recommendation"
|
||||
options={[
|
||||
{ value: 'low', label: t('editGasLow'), recommended: false },
|
||||
{
|
||||
value: 'medium',
|
||||
label: t('editGasMedium'),
|
||||
recommended: false,
|
||||
value: GAS_RECOMMENDATIONS.LOW,
|
||||
label: t('editGasLow'),
|
||||
recommended: defaultEstimateToUse === GAS_RECOMMENDATIONS.LOW,
|
||||
},
|
||||
{
|
||||
value: GAS_RECOMMENDATIONS.MEDIUM,
|
||||
label: t('editGasMedium'),
|
||||
recommended:
|
||||
defaultEstimateToUse === GAS_RECOMMENDATIONS.MEDIUM,
|
||||
},
|
||||
{
|
||||
value: GAS_RECOMMENDATIONS.HIGH,
|
||||
label: t('editGasHigh'),
|
||||
recommended: defaultEstimateToUse === GAS_RECOMMENDATIONS.HIGH,
|
||||
},
|
||||
{ value: 'high', label: t('editGasHigh'), recommended: true },
|
||||
]}
|
||||
selectedValue="high"
|
||||
selectedValue={estimateToUse}
|
||||
onChange={setEstimateToUse}
|
||||
/>
|
||||
)}
|
||||
{!requireDappAcknowledgement && !alwaysShowForm && (
|
||||
{!alwaysShowForm && (
|
||||
<button
|
||||
className="edit-gas-display__advanced-button"
|
||||
onClick={() => setShowAdvancedForm(!showAdvancedForm)}
|
||||
@ -112,8 +179,29 @@ export default function EditGasDisplay({
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{((!requireDappAcknowledgement && alwaysShowForm) ||
|
||||
showAdvancedForm) && <AdvancedGasControls />}
|
||||
{!requireDappAcknowledgement &&
|
||||
(alwaysShowForm || showAdvancedForm) && (
|
||||
<AdvancedGasControls
|
||||
gasFeeEstimates={gasFeeEstimates}
|
||||
gasEstimateType={gasEstimateType}
|
||||
estimateToUse={estimateToUse}
|
||||
isGasEstimatesLoading={isGasEstimatesLoading}
|
||||
gasLimit={gasLimit}
|
||||
setGasLimit={setGasLimit}
|
||||
maxPriorityFee={maxPriorityFeePerGas}
|
||||
setMaxPriorityFee={setMaxPriorityFeePerGas}
|
||||
maxFee={maxFeePerGas}
|
||||
setMaxFee={setMaxFeePerGas}
|
||||
gasPrice={gasPrice}
|
||||
setGasPrice={setGasPrice}
|
||||
maxPriorityFeeFiat={maxPriorityFeePerGasFiat}
|
||||
maxFeeFiat={maxFeePerGasFiat}
|
||||
maxPriorityFeeError={
|
||||
isMaxPriorityFeeError ? t('editGasMaxPriorityFeeLow') : null
|
||||
}
|
||||
maxFeeError={isMaxFeeError ? t('editGasMaxFeeLow') : null}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!requireDappAcknowledgement && showEducationButton && (
|
||||
<div className="edit-gas-display__education">
|
||||
@ -133,6 +221,7 @@ EditGasDisplay.propTypes = {
|
||||
onEducationClick: PropTypes.func,
|
||||
dappSuggestedGasFee: PropTypes.number,
|
||||
dappOrigin: PropTypes.string,
|
||||
defaultEstimateToUse: PropTypes.oneOf(Object.values(GAS_RECOMMENDATIONS)),
|
||||
};
|
||||
|
||||
EditGasDisplay.defaultProps = {
|
||||
|
@ -1,5 +1,6 @@
|
||||
.edit-gas-display {
|
||||
& .actionable-message--warning {
|
||||
& .actionable-message--warning,
|
||||
& .actionable-message--error {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ export default function TransactionTotalBanner({ total, detail, timing }) {
|
||||
return (
|
||||
<div className="transaction-total-banner">
|
||||
<Typography color={COLORS.BLACK} variant={TYPOGRAPHY.H1}>
|
||||
{total}
|
||||
~ {total}
|
||||
</Typography>
|
||||
{detail && (
|
||||
<Typography
|
||||
|
@ -4,7 +4,13 @@ import PropTypes from 'prop-types';
|
||||
import Typography from '../typography/typography';
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function NumericInput({ detailText, value, onChange, error }) {
|
||||
export default function NumericInput({
|
||||
detailText,
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
autoFocus,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={classNames('numeric-input', { 'numeric-input--error': error })}
|
||||
@ -12,8 +18,9 @@ export default function NumericInput({ detailText, value, onChange, error }) {
|
||||
<input
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={(e) => onChange?.(Number(e.target.value))}
|
||||
onChange={(e) => onChange?.(parseInt(e.target.value, 10))}
|
||||
min="0"
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
{detailText && (
|
||||
<Typography color={COLORS.UI4} variant={TYPOGRAPHY.H7} tag="span">
|
||||
@ -29,6 +36,7 @@ NumericInput.propTypes = {
|
||||
detailText: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
error: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
};
|
||||
|
||||
NumericInput.defaultProps = {
|
||||
@ -36,4 +44,5 @@ NumericInput.defaultProps = {
|
||||
detailText: '',
|
||||
onChange: undefined,
|
||||
error: '',
|
||||
autoFocus: false,
|
||||
};
|
||||
|
@ -8,6 +8,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__column {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__column-recommended {
|
||||
height: 20px;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export default function RadioGroup({ options, name, selectedValue, onChange }) {
|
||||
return (
|
||||
<div className="radio-group">
|
||||
{options.map((option) => {
|
||||
const checked = option.value === selectedValue;
|
||||
return (
|
||||
<div className="radio-group__column" key={`${name}-${option.value}`}>
|
||||
<label>
|
||||
@ -29,7 +30,8 @@ export default function RadioGroup({ options, name, selectedValue, onChange }) {
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
defaultChecked={option.value === selectedValue}
|
||||
defaultChecked={checked}
|
||||
checked={checked}
|
||||
value={option.value}
|
||||
onChange={() => onChange?.(option.value)}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user