Allow submission of transactions with dapp suggested gas fees, while estimates are loading (#11858)

* Allow submission of transactions with dapp suggested gas fees, while estimates are loading

* Allow editing of transactions with dapp suggested feeds, while estimates are still loading

* Ensure that advanced gas is always editible inline when gas is loading

* Ensure that insufficient balance error is shown when gas is loading if the user has customized the gas

* Only set gas price insufficient errors if the current network is non-eip-1559, or the txparams actually have a gas price

* Remove unnecessary param

* lint fix

* ensure insufficient balance warning is showing when loading

* Ensure that eip1559 network transactions do not combined eip1559 and non-eip1559 gas fee properties

* Lint fix
feature/default_network_editable
Dan J Miller 3 years ago committed by ryanml
parent da42d60df1
commit 17ae98ac39
  1. 20
      shared/modules/transaction.utils.js
  2. 12
      ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
  3. 9
      ui/components/app/edit-gas-display/edit-gas-display.component.js
  4. 2
      ui/components/app/edit-gas-display/index.scss
  5. 16
      ui/components/app/edit-gas-popover/edit-gas-popover.component.js
  6. 5
      ui/hooks/useGasFeeInputs.js
  7. 9
      ui/pages/confirm-transaction-base/confirm-transaction-base.container.js

@ -38,3 +38,23 @@ export function isLegacyTransaction(transaction) {
isHexString(transaction.txParams.gasPrice)) isHexString(transaction.txParams.gasPrice))
); );
} }
/**
* Determine if a transactions gas fees in txParams match those in its dappSuggestedGasFees property
* @param {import("../constants/transaction").TransactionMeta} transaction -
* the transaction to check
* @returns {boolean} true if both the txParams and dappSuggestedGasFees are objects with truthy gas fee properties,
* and those properties are strictly equal
*/
export function txParamsAreDappSuggested(transaction) {
const { gasPrice, maxPriorityFeePerGas, maxFeePerGas } =
transaction?.txParams || {};
return (
(gasPrice && gasPrice === transaction?.dappSuggestedGasFees?.gasPrice) ||
(maxPriorityFeePerGas &&
maxFeePerGas &&
transaction?.dappSuggestedGasFees?.maxPriorityFeePerGas ===
maxPriorityFeePerGas &&
transaction?.dappSuggestedGasFees?.maxFeePerGas === maxFeePerGas)
);
}

@ -8,7 +8,6 @@ import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
import { getGasFormErrorText } from '../../../helpers/constants/gas'; import { getGasFormErrorText } from '../../../helpers/constants/gas';
import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask'; import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask';
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
export default function AdvancedGasControls({ export default function AdvancedGasControls({
gasEstimateType, gasEstimateType,
@ -25,19 +24,12 @@ export default function AdvancedGasControls({
maxFeeFiat, maxFeeFiat,
gasErrors, gasErrors,
minimumGasLimit, minimumGasLimit,
estimateToUse,
}) { }) {
const t = useContext(I18nContext); const t = useContext(I18nContext);
const networkAndAccountSupport1559 = useSelector( const networkAndAccountSupport1559 = useSelector(
checkNetworkAndAccountSupports1559, checkNetworkAndAccountSupports1559,
); );
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading); const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
const isGasLoadingAnimationIsShowing = useSelector(
getGasLoadingAnimationIsShowing,
);
const disableFormFields =
estimateToUse !== 'custom' &&
(isGasEstimatesLoading || isGasLoadingAnimationIsShowing);
const showFeeMarketFields = const showFeeMarketFields =
networkAndAccountSupport1559 && networkAndAccountSupport1559 &&
@ -82,7 +74,6 @@ export default function AdvancedGasControls({
? getGasFormErrorText(gasErrors.maxPriorityFee, t) ? getGasFormErrorText(gasErrors.maxPriorityFee, t)
: null : null
} }
disabled={disableFormFields}
/> />
<FormField <FormField
titleText={t('maxFee')} titleText={t('maxFee')}
@ -100,7 +91,6 @@ export default function AdvancedGasControls({
? getGasFormErrorText(gasErrors.maxFee, t) ? getGasFormErrorText(gasErrors.maxFee, t)
: null : null
} }
disabled={disableFormFields}
/> />
</> </>
) : ( ) : (
@ -120,7 +110,6 @@ export default function AdvancedGasControls({
? getGasFormErrorText(gasErrors.gasPrice, t) ? getGasFormErrorText(gasErrors.gasPrice, t)
: null : null
} }
disabled={disableFormFields}
/> />
</> </>
)} )}
@ -143,5 +132,4 @@ AdvancedGasControls.propTypes = {
maxFeeFiat: PropTypes.string, maxFeeFiat: PropTypes.string,
gasErrors: PropTypes.object, gasErrors: PropTypes.object,
minimumGasLimit: PropTypes.number, minimumGasLimit: PropTypes.number,
estimateToUse: PropTypes.bool,
}; };

@ -67,6 +67,7 @@ export default function EditGasDisplay({
balanceError, balanceError,
estimatesUnavailableWarning, estimatesUnavailableWarning,
hasGasErrors, hasGasErrors,
txParamsHaveBeenCustomized,
}) { }) {
const t = useContext(I18nContext); const t = useContext(I18nContext);
const isMainnet = useSelector(getIsMainnet); const isMainnet = useSelector(getIsMainnet);
@ -96,7 +97,9 @@ export default function EditGasDisplay({
dappSuggestedAndTxParamGasFeesAreTheSame, dappSuggestedAndTxParamGasFeesAreTheSame,
); );
const showTopError = balanceError || estimatesUnavailableWarning; const showTopError =
(balanceError || estimatesUnavailableWarning) &&
(!isGasEstimatesLoading || txParamsHaveBeenCustomized);
const radioButtonsEnabled = const radioButtonsEnabled =
networkAndAccountSupport1559 && networkAndAccountSupport1559 &&
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET && gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET &&
@ -120,7 +123,7 @@ export default function EditGasDisplay({
/> />
</div> </div>
)} )}
{showTopError && !isGasEstimatesLoading && ( {showTopError && (
<div className="edit-gas-display__warning"> <div className="edit-gas-display__warning">
<ErrorMessage errorKey={errorKey} /> <ErrorMessage errorKey={errorKey} />
</div> </div>
@ -266,7 +269,6 @@ export default function EditGasDisplay({
gasErrors={gasErrors} gasErrors={gasErrors}
onManualChange={onManualChange} onManualChange={onManualChange}
minimumGasLimit={minimumGasLimit} minimumGasLimit={minimumGasLimit}
estimateToUse={estimateToUse}
/> />
)} )}
</div> </div>
@ -317,4 +319,5 @@ EditGasDisplay.propTypes = {
balanceError: PropTypes.bool, balanceError: PropTypes.bool,
estimatesUnavailableWarning: PropTypes.bool, estimatesUnavailableWarning: PropTypes.bool,
hasGasErrors: PropTypes.bool, hasGasErrors: PropTypes.bool,
txParamsHaveBeenCustomized: PropTypes.bool,
}; };

@ -64,5 +64,7 @@
&__warning { &__warning {
margin-bottom: 24px; margin-bottom: 24px;
position: relative;
margin-top: 4px;
} }
} }

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs'; import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
import { txParamsAreDappSuggested } from '../../../../shared/modules/transaction.utils';
import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas'; import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas';
import { import {
@ -93,6 +93,9 @@ export default function EditGasPopover({
estimatedBaseFee, estimatedBaseFee,
} = useGasFeeInputs(defaultEstimateToUse, transaction, minimumGasLimit, mode); } = useGasFeeInputs(defaultEstimateToUse, transaction, minimumGasLimit, mode);
const txParamsHaveBeenCustomized =
estimateToUse === 'custom' || txParamsAreDappSuggested(transaction);
/** /**
* Temporary placeholder, this should be managed by the parent component but * Temporary placeholder, this should be managed by the parent component but
* we will be extracting this component from the hard to maintain modal/ * we will be extracting this component from the hard to maintain modal/
@ -129,11 +132,17 @@ export default function EditGasPopover({
gasPrice: decGWEIToHexWEI(gasPrice), gasPrice: decGWEIToHexWEI(gasPrice),
}; };
const cleanTransactionParams = { ...transaction.txParams };
if (networkAndAccountSupport1559) {
delete cleanTransactionParams.gasPrice;
}
const updatedTxMeta = { const updatedTxMeta = {
...transaction, ...transaction,
userFeeLevel: estimateToUse || 'custom', userFeeLevel: estimateToUse || 'custom',
txParams: { txParams: {
...transaction.txParams, ...cleanTransactionParams,
...newGasSettings, ...newGasSettings,
}, },
}; };
@ -212,7 +221,7 @@ export default function EditGasPopover({
hasGasErrors || hasGasErrors ||
balanceError || balanceError ||
((isGasEstimatesLoading || gasLoadingAnimationIsShowing) && ((isGasEstimatesLoading || gasLoadingAnimationIsShowing) &&
!estimateToUse === 'custom') !txParamsHaveBeenCustomized)
} }
> >
{footerButtonText} {footerButtonText}
@ -262,6 +271,7 @@ export default function EditGasPopover({
balanceError={balanceError} balanceError={balanceError}
estimatesUnavailableWarning={estimatesUnavailableWarning} estimatesUnavailableWarning={estimatesUnavailableWarning}
hasGasErrors={hasGasErrors} hasGasErrors={hasGasErrors}
txParamsHaveBeenCustomized={txParamsHaveBeenCustomized}
{...editGasDisplayProps} {...editGasDisplayProps}
/> />
</> </>

@ -456,7 +456,10 @@ export function useGasFeeInputs(
if (networkAndAccountSupports1559) { if (networkAndAccountSupports1559) {
estimatesUnavailableWarning = true; estimatesUnavailableWarning = true;
} }
if (bnLessThanEqualTo(gasPriceToUse, 0)) { if (
(!networkAndAccountSupports1559 || transaction?.txParams?.gasPrice) &&
bnLessThanEqualTo(gasPriceToUse, 0)
) {
gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW; gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW;
} }
break; break;

@ -31,7 +31,10 @@ import {
getPreferences, getPreferences,
} from '../../selectors'; } from '../../selectors';
import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { getMostRecentOverviewPage } from '../../ducks/history/history';
import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils'; import {
transactionMatchesNetwork,
txParamsAreDappSuggested,
} from '../../../shared/modules/transaction.utils';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { import {
updateTransactionGasFees, updateTransactionGasFees,
@ -155,7 +158,9 @@ const mapStateToProps = (state, ownProps) => {
const isEthGasPrice = getIsEthGasPriceFetched(state); const isEthGasPrice = getIsEthGasPriceFetched(state);
const noGasPrice = !supportsEIP1599 && getNoGasPriceFetched(state); const noGasPrice = !supportsEIP1599 && getNoGasPriceFetched(state);
const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state); const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state);
const gasFeeIsCustom = fullTxData.userFeeLevel === 'custom'; const gasFeeIsCustom =
fullTxData.userFeeLevel === 'custom' ||
txParamsAreDappSuggested(fullTxData);
return { return {
balance, balance,

Loading…
Cancel
Save