From f1fc51667a485ac769134e1540d13cdc69631f1f Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Wed, 28 Apr 2021 14:02:01 -0400
Subject: [PATCH] Handling gas price fetch failure (#10767)
---
app/_locales/en/messages.json | 6 ++
...onfirm-page-container-content.component.js | 5 ++
.../confirm-page-container-warning/index.scss | 1 -
.../confirm-page-container.component.js | 3 +
.../gas-modal-page-container.component.js | 30 ++++---
ui/app/ducks/gas/gas-duck.test.js | 61 ++++++++-------
ui/app/ducks/gas/gas.duck.js | 78 ++++++++++++-------
ui/app/helpers/constants/error-keys.js | 3 +
.../pages/confirm-approve/confirm-approve.js | 12 ++-
.../confirm-transaction-base.component.js | 61 +++++++++++++--
.../confirm-transaction-base.container.js | 14 +++-
.../send-content/send-content.component.js | 33 ++++++--
.../send-content/send-content.container.js | 4 +
.../send-gas-row/send-gas-row.component.js | 21 ++++-
.../send-gas-row/send-gas-row.container.js | 6 ++
.../send/send-footer/send-footer.component.js | 5 +-
.../send-footer/send-footer.component.test.js | 1 +
.../send/send-footer/send-footer.container.js | 2 +
ui/app/selectors/custom-gas.js | 22 +++++-
19 files changed, 274 insertions(+), 94 deletions(-)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index f98bd83a4..69fd51a1d 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -684,6 +684,9 @@
"estimatedProcessingTimes": {
"message": "Estimated Processing Times"
},
+ "ethGasPriceFetchWarning": {
+ "message": "Backup gas price is provided as the main gas estimation service is unavailable right now."
+ },
"eth_accounts": {
"message": "View the addresses of your permitted accounts (required)",
"description": "The description for the `eth_accounts` permission"
@@ -780,6 +783,9 @@
"gasPriceExtremelyLow": {
"message": "Gas Price Extremely Low"
},
+ "gasPriceFetchFailed": {
+ "message": "Gas price estimation failed due to network error."
+ },
"gasPriceInfoTooltipContent": {
"message": "Gas price specifies the amount of Ether you are willing to pay for each unit of gas."
},
diff --git a/ui/app/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js b/ui/app/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
index 9009f9feb..e47bfaf78 100644
--- a/ui/app/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
+++ b/ui/app/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
@@ -22,6 +22,7 @@ export default class ConfirmPageContainerContent extends Component {
titleComponent: PropTypes.node,
warning: PropTypes.string,
origin: PropTypes.string.isRequired,
+ ethGasPriceWarning: PropTypes.string,
// Footer
onCancelAll: PropTypes.func,
onCancel: PropTypes.func,
@@ -81,11 +82,15 @@ export default class ConfirmPageContainerContent extends Component {
unapprovedTxCount,
rejectNText,
origin,
+ ethGasPriceWarning,
} = this.props;
return (
{warning &&
}
+ {ethGasPriceWarning && (
+
+ )}
)}
{contentComponent && (
diff --git a/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js
index f57cce92d..ea59b7882 100644
--- a/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js
+++ b/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js
@@ -123,19 +123,25 @@ export default class GasModalPageContainer extends Component {
infoRowProps: { newTotalFiat, newTotalEth, sendAmount, transactionFee },
} = this.props;
- let tabsToRender = [
- {
- name: this.context.t('basic'),
- content: this.renderBasicTabContent(gasPriceButtonGroupProps),
- },
- {
- name: this.context.t('advanced'),
- content: this.renderAdvancedTabContent(),
- },
- ];
-
+ let tabsToRender;
if (hideBasic) {
- tabsToRender = tabsToRender.slice(1);
+ tabsToRender = [
+ {
+ name: this.context.t('advanced'),
+ content: this.renderAdvancedTabContent(),
+ },
+ ];
+ } else {
+ tabsToRender = [
+ {
+ name: this.context.t('basic'),
+ content: this.renderBasicTabContent(gasPriceButtonGroupProps),
+ },
+ {
+ name: this.context.t('advanced'),
+ content: this.renderAdvancedTabContent(),
+ },
+ ];
}
return (
diff --git a/ui/app/ducks/gas/gas-duck.test.js b/ui/app/ducks/gas/gas-duck.test.js
index eab7a74b6..e4e51beab 100644
--- a/ui/app/ducks/gas/gas-duck.test.js
+++ b/ui/app/ducks/gas/gas-duck.test.js
@@ -3,8 +3,7 @@ import sinon from 'sinon';
import BN from 'bn.js';
import GasReducer, {
- basicGasEstimatesLoadingStarted,
- basicGasEstimatesLoadingFinished,
+ setBasicEstimateStatus,
setBasicGasEstimateData,
setCustomGasPrice,
setCustomGasLimit,
@@ -49,7 +48,8 @@ describe('Gas Duck', () => {
fast: null,
safeLow: null,
},
- basicEstimateIsLoading: true,
+ basicEstimateStatus: 'LOADING',
+ estimateSource: '',
};
const providerState = {
@@ -61,14 +61,12 @@ describe('Gas Duck', () => {
type: 'mainnet',
};
- const BASIC_GAS_ESTIMATE_LOADING_FINISHED =
- 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED';
- const BASIC_GAS_ESTIMATE_LOADING_STARTED =
- 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_STARTED';
+ const BASIC_GAS_ESTIMATE_STATUS = 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS';
const SET_BASIC_GAS_ESTIMATE_DATA =
'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA';
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT';
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE';
+ const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE';
describe('GasReducer()', () => {
it('should initialize state', () => {
@@ -84,16 +82,13 @@ describe('Gas Duck', () => {
).toStrictEqual(mockState);
});
- it('should set basicEstimateIsLoading to true when receiving a BASIC_GAS_ESTIMATE_LOADING_STARTED action', () => {
+ it('should set basicEstimateStatus to LOADING when receiving a BASIC_GAS_ESTIMATE_STATUS action with value LOADING', () => {
expect(
- GasReducer(mockState, { type: BASIC_GAS_ESTIMATE_LOADING_STARTED }),
- ).toStrictEqual({ basicEstimateIsLoading: true, ...mockState });
- });
-
- it('should set basicEstimateIsLoading to false when receiving a BASIC_GAS_ESTIMATE_LOADING_FINISHED action', () => {
- expect(
- GasReducer(mockState, { type: BASIC_GAS_ESTIMATE_LOADING_FINISHED }),
- ).toStrictEqual({ basicEstimateIsLoading: false, ...mockState });
+ GasReducer(mockState, {
+ type: BASIC_GAS_ESTIMATE_STATUS,
+ value: 'LOADING',
+ }),
+ ).toStrictEqual({ basicEstimateStatus: 'LOADING', ...mockState });
});
it('should set basicEstimates when receiving a SET_BASIC_GAS_ESTIMATE_DATA action', () => {
@@ -127,18 +122,17 @@ describe('Gas Duck', () => {
});
});
- describe('basicGasEstimatesLoadingStarted', () => {
- it('should create the correct action', () => {
- expect(basicGasEstimatesLoadingStarted()).toStrictEqual({
- type: BASIC_GAS_ESTIMATE_LOADING_STARTED,
- });
- });
+ it('should set estimateSource to Metaswaps when receiving a SET_ESTIMATE_SOURCE action with value Metaswaps', () => {
+ expect(
+ GasReducer(mockState, { type: SET_ESTIMATE_SOURCE, value: 'Metaswaps' }),
+ ).toStrictEqual({ estimateSource: 'Metaswaps', ...mockState });
});
- describe('basicGasEstimatesLoadingFinished', () => {
+ describe('basicEstimateStatus', () => {
it('should create the correct action', () => {
- expect(basicGasEstimatesLoadingFinished()).toStrictEqual({
- type: BASIC_GAS_ESTIMATE_LOADING_FINISHED,
+ expect(setBasicEstimateStatus('LOADING')).toStrictEqual({
+ type: BASIC_GAS_ESTIMATE_STATUS,
+ value: 'LOADING',
});
});
});
@@ -158,7 +152,7 @@ describe('Gas Duck', () => {
}));
expect(mockDistpatch.getCall(0).args).toStrictEqual([
- { type: BASIC_GAS_ESTIMATE_LOADING_STARTED },
+ { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'LOADING' },
]);
expect(
@@ -168,7 +162,11 @@ describe('Gas Duck', () => {
).toStrictEqual(true);
expect(mockDistpatch.getCall(2).args).toStrictEqual([
- { type: BASIC_GAS_ESTIMATE_LOADING_FINISHED },
+ { type: 'metamask/gas/SET_ESTIMATE_SOURCE', value: 'MetaSwaps' },
+ ]);
+
+ expect(mockDistpatch.getCall(4).args).toStrictEqual([
+ { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'READY' },
]);
});
@@ -190,9 +188,12 @@ describe('Gas Duck', () => {
metamask: { provider: { ...providerStateForTestNetwork } },
}));
expect(mockDistpatch.getCall(0).args).toStrictEqual([
- { type: BASIC_GAS_ESTIMATE_LOADING_STARTED },
+ { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'LOADING' },
]);
expect(mockDistpatch.getCall(1).args).toStrictEqual([
+ { type: 'metamask/gas/SET_ESTIMATE_SOURCE', value: 'eth_gasprice' },
+ ]);
+ expect(mockDistpatch.getCall(2).args).toStrictEqual([
{
type: SET_BASIC_GAS_ESTIMATE_DATA,
value: {
@@ -200,8 +201,8 @@ describe('Gas Duck', () => {
},
},
]);
- expect(mockDistpatch.getCall(2).args).toStrictEqual([
- { type: BASIC_GAS_ESTIMATE_LOADING_FINISHED },
+ expect(mockDistpatch.getCall(3).args).toStrictEqual([
+ { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'READY' },
]);
});
});
diff --git a/ui/app/ducks/gas/gas.duck.js b/ui/app/ducks/gas/gas.duck.js
index 5785b2cf3..48d277443 100644
--- a/ui/app/ducks/gas/gas.duck.js
+++ b/ui/app/ducks/gas/gas.duck.js
@@ -8,15 +8,24 @@ import {
import { getIsMainnet, getCurrentChainId } from '../../selectors';
import fetchWithCache from '../../helpers/utils/fetch-with-cache';
+const BASIC_ESTIMATE_STATES = {
+ LOADING: 'LOADING',
+ FAILED: 'FAILED',
+ READY: 'READY',
+};
+
+const GAS_SOURCE = {
+ METASWAPS: 'MetaSwaps',
+ ETHGASPRICE: 'eth_gasprice',
+};
+
// Actions
-const BASIC_GAS_ESTIMATE_LOADING_FINISHED =
- 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED';
-const BASIC_GAS_ESTIMATE_LOADING_STARTED =
- 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_STARTED';
+const BASIC_GAS_ESTIMATE_STATUS = 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS';
const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA';
const SET_BASIC_GAS_ESTIMATE_DATA = 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA';
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT';
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE';
+const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE';
const initState = {
customData: {
@@ -28,21 +37,17 @@ const initState = {
average: null,
fast: null,
},
- basicEstimateIsLoading: true,
+ basicEstimateStatus: BASIC_ESTIMATE_STATES.LOADING,
+ estimateSource: '',
};
// Reducer
export default function reducer(state = initState, action) {
switch (action.type) {
- case BASIC_GAS_ESTIMATE_LOADING_STARTED:
+ case BASIC_GAS_ESTIMATE_STATUS:
return {
...state,
- basicEstimateIsLoading: true,
- };
- case BASIC_GAS_ESTIMATE_LOADING_FINISHED:
- return {
- ...state,
- basicEstimateIsLoading: false,
+ basicEstimateStatus: action.value,
};
case SET_BASIC_GAS_ESTIMATE_DATA:
return {
@@ -70,21 +75,21 @@ export default function reducer(state = initState, action) {
...state,
customData: cloneDeep(initState.customData),
};
+ case SET_ESTIMATE_SOURCE:
+ return {
+ ...state,
+ estimateSource: action.value,
+ };
default:
return state;
}
}
// Action Creators
-export function basicGasEstimatesLoadingStarted() {
+export function setBasicEstimateStatus(status) {
return {
- type: BASIC_GAS_ESTIMATE_LOADING_STARTED,
- };
-}
-
-export function basicGasEstimatesLoadingFinished() {
- return {
- type: BASIC_GAS_ESTIMATE_LOADING_FINISHED,
+ type: BASIC_GAS_ESTIMATE_STATUS,
+ value: status,
};
}
@@ -106,18 +111,26 @@ export function fetchBasicGasEstimates() {
return async (dispatch, getState) => {
const isMainnet = getIsMainnet(getState());
- dispatch(basicGasEstimatesLoadingStarted());
-
+ dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.LOADING));
let basicEstimates;
- if (isMainnet || process.env.IN_TEST) {
- basicEstimates = await fetchExternalBasicGasEstimates();
- } else {
- basicEstimates = await fetchEthGasPriceEstimates(getState());
+ try {
+ dispatch(setEstimateSource(GAS_SOURCE.ETHGASPRICE));
+ if (isMainnet || process.env.IN_TEST) {
+ try {
+ basicEstimates = await fetchExternalBasicGasEstimates();
+ dispatch(setEstimateSource(GAS_SOURCE.METASWAPS));
+ } catch (error) {
+ basicEstimates = await fetchEthGasPriceEstimates(getState());
+ }
+ } else {
+ basicEstimates = await fetchEthGasPriceEstimates(getState());
+ }
+ dispatch(setBasicGasEstimateData(basicEstimates));
+ dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.READY));
+ } catch (error) {
+ dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.FAILED));
}
- dispatch(setBasicGasEstimateData(basicEstimates));
- dispatch(basicGasEstimatesLoadingFinished());
-
return basicEstimates;
};
}
@@ -211,3 +224,10 @@ export function setCustomGasLimit(newLimit) {
export function resetCustomData() {
return { type: RESET_CUSTOM_DATA };
}
+
+export function setEstimateSource(estimateSource) {
+ return {
+ type: SET_ESTIMATE_SOURCE,
+ value: estimateSource,
+ };
+}
diff --git a/ui/app/helpers/constants/error-keys.js b/ui/app/helpers/constants/error-keys.js
index 85ce13d7d..bfdb7474f 100644
--- a/ui/app/helpers/constants/error-keys.js
+++ b/ui/app/helpers/constants/error-keys.js
@@ -2,3 +2,6 @@ export const INSUFFICIENT_FUNDS_ERROR_KEY = 'insufficientFunds';
export const GAS_LIMIT_TOO_LOW_ERROR_KEY = 'gasLimitTooLow';
export const TRANSACTION_ERROR_KEY = 'transactionError';
export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract';
+export const ETH_GAS_PRICE_FETCH_WARNING_KEY = 'ethGasPriceFetchWarning';
+export const GAS_PRICE_FETCH_FAILURE_ERROR_KEY = 'gasPriceFetchFailed';
+export const GAS_PRICE_EXCESSIVE_ERROR_KEY = 'gasPriceExcessive';
diff --git a/ui/app/pages/confirm-approve/confirm-approve.js b/ui/app/pages/confirm-approve/confirm-approve.js
index 9eb51bd2a..b7731e752 100644
--- a/ui/app/pages/confirm-approve/confirm-approve.js
+++ b/ui/app/pages/confirm-approve/confirm-approve.js
@@ -24,6 +24,8 @@ import {
getUseNonceField,
getCustomNonceValue,
getNextSuggestedNonce,
+ getNoGasPriceFetched,
+ getIsEthGasPriceFetched,
} from '../../selectors';
import { currentNetworkTxListSelector } from '../../selectors/transactions';
import Loading from '../../components/ui/loading-screen';
@@ -116,6 +118,8 @@ export default function ConfirmApprove() {
const customData = customPermissionAmount
? getCustomTxParamsData(data, { customPermissionAmount, decimals })
: null;
+ const isEthGasPrice = useSelector(getIsEthGasPriceFetched);
+ const noGasPrice = useSelector(getNoGasPriceFetched);
return tokenSymbol === undefined ? (
@@ -136,7 +140,13 @@ export default function ConfirmApprove() {
tokenSymbol={tokenSymbol}
tokenBalance={tokenBalance}
showCustomizeGasModal={() =>
- dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData }))
+ dispatch(
+ showModal({
+ name: 'CUSTOMIZE_GAS',
+ txData,
+ hideBasic: isEthGasPrice || noGasPrice,
+ }),
+ )
}
showEditApprovalPermissionModal={({
/* eslint-disable no-shadow */
diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
index 54646ac64..9a2cab98c 100644
--- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -12,6 +12,8 @@ import {
INSUFFICIENT_FUNDS_ERROR_KEY,
TRANSACTION_ERROR_KEY,
GAS_LIMIT_TOO_LOW_ERROR_KEY,
+ ETH_GAS_PRICE_FETCH_WARNING_KEY,
+ GAS_PRICE_FETCH_FAILURE_ERROR_KEY,
} from '../../helpers/constants/error-keys';
import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display';
import { PRIMARY, SECONDARY } from '../../helpers/constants/common';
@@ -23,6 +25,7 @@ import {
TRANSACTION_STATUSES,
} from '../../../../shared/constants/transaction';
import { getTransactionTypeTitle } from '../../helpers/utils/transactions.util';
+import ErrorMessage from '../../components/ui/error-message';
export default class ConfirmTransactionBase extends Component {
static contextTypes = {
@@ -95,12 +98,15 @@ export default class ConfirmTransactionBase extends Component {
showAccountInHeader: PropTypes.bool,
mostRecentOverviewPage: PropTypes.string.isRequired,
isMainnet: PropTypes.bool,
+ isEthGasPrice: PropTypes.bool,
+ noGasPrice: PropTypes.bool,
};
state = {
submitting: false,
submitError: null,
submitWarning: '',
+ ethGasPriceWarning: '',
};
componentDidUpdate(prevProps) {
@@ -114,12 +120,14 @@ export default class ConfirmTransactionBase extends Component {
customNonceValue,
toAddress,
tryReverseResolveAddress,
+ isEthGasPrice,
} = this.props;
const {
customNonceValue: prevCustomNonceValue,
nextNonce: prevNextNonce,
toAddress: prevToAddress,
transactionStatus: prevTxStatus,
+ isEthGasPrice: prevIsEthGasPrice,
} = prevProps;
const statusUpdated = transactionStatus !== prevTxStatus;
const txDroppedOrConfirmed =
@@ -151,6 +159,18 @@ export default class ConfirmTransactionBase extends Component {
if (toAddress && toAddress !== prevToAddress) {
tryReverseResolveAddress(toAddress);
}
+
+ if (isEthGasPrice !== prevIsEthGasPrice) {
+ if (isEthGasPrice) {
+ this.setState({
+ ethGasPriceWarning: this.context.t(ETH_GAS_PRICE_FETCH_WARNING_KEY),
+ });
+ } else {
+ this.setState({
+ ethGasPriceWarning: '',
+ });
+ }
+ }
}
getErrorKey() {
@@ -160,6 +180,7 @@ export default class ConfirmTransactionBase extends Component {
hexTransactionFee,
txData: { simulationFails, txParams: { value: amount } = {} } = {},
customGas,
+ noGasPrice,
} = this.props;
const insufficientBalance =
@@ -194,6 +215,13 @@ export default class ConfirmTransactionBase extends Component {
};
}
+ if (noGasPrice) {
+ return {
+ valid: false,
+ errorKey: GAS_PRICE_FETCH_FAILURE_ERROR_KEY,
+ };
+ }
+
return {
valid: true,
};
@@ -243,9 +271,12 @@ export default class ConfirmTransactionBase extends Component {
nextNonce,
getNextNonce,
isMainnet,
+ isEthGasPrice,
+ noGasPrice,
} = this.props;
const notMainnetOrTest = !(isMainnet || process.env.IN_TEST);
+ const gasPriceFetchFailure = isEthGasPrice || noGasPrice;
return (
@@ -253,18 +284,26 @@ export default class ConfirmTransactionBase extends Component {
this.handleEditGas()
}
- onHeaderClick={notMainnetOrTest ? null : () => this.handleEditGas()}
secondaryText={
hideFiatConversion
? this.context.t('noConversionRateAvailable')
: ''
}
/>
- {advancedInlineGasShown || notMainnetOrTest ? (
+ {advancedInlineGasShown ||
+ notMainnetOrTest ||
+ gasPriceFetchFailure ? (
updateGasAndCalculate({ ...customGas, gasPrice: newGasPrice })
@@ -279,6 +318,11 @@ export default class ConfirmTransactionBase extends Component {
isSpeedUp={false}
/>
) : null}
+ {noGasPrice ? (
+
+
+
+ ) : null}
this.handleSubmit()}
hideSenderToRecipient={hideSenderToRecipient}
origin={txData.origin}
+ ethGasPriceWarning={ethGasPriceWarning}
/>
);
}
diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js
index 7a761bd9b..bf32a6697 100644
--- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js
@@ -37,6 +37,8 @@ import {
getUseNonceField,
getPreferences,
transactionFeeSelector,
+ getNoGasPriceFetched,
+ getIsEthGasPriceFetched,
} from '../../selectors';
import { getMostRecentOverviewPage } from '../../ducks/history/history';
import { transactionMatchesNetwork } from '../../../../shared/modules/transaction.utils';
@@ -150,6 +152,8 @@ const mapStateToProps = (state, ownProps) => {
};
}
customNonceValue = getCustomNonceValue(state);
+ const isEthGasPrice = getIsEthGasPriceFetched(state);
+ const noGasPrice = getNoGasPriceFetched(state);
return {
balance,
@@ -189,6 +193,8 @@ const mapStateToProps = (state, ownProps) => {
nextNonce,
mostRecentOverviewPage: getMostRecentOverviewPage(state),
isMainnet,
+ isEthGasPrice,
+ noGasPrice,
};
};
@@ -207,7 +213,12 @@ export const mapDispatchToProps = (dispatch) => {
},
showCustomizeGasModal: ({ txData, onSubmit, validate }) => {
return dispatch(
- showModal({ name: 'CUSTOMIZE_GAS', txData, onSubmit, validate }),
+ showModal({
+ name: 'CUSTOMIZE_GAS',
+ txData,
+ onSubmit,
+ validate,
+ }),
);
},
updateGasAndCalculate: (updatedTx) => {
@@ -278,6 +289,7 @@ const getValidateEditGas = ({ balance, conversionRate, txData }) => {
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { balance, conversionRate, txData, unapprovedTxs } = stateProps;
+
const {
cancelAllTransactions: dispatchCancelAllTransactions,
showCustomizeGasModal: dispatchShowCustomizeGasModal,
diff --git a/ui/app/pages/send/send-content/send-content.component.js b/ui/app/pages/send/send-content/send-content.component.js
index 6f4fd7924..6f1a82b92 100644
--- a/ui/app/pages/send/send-content/send-content.component.js
+++ b/ui/app/pages/send/send-content/send-content.component.js
@@ -2,6 +2,11 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PageContainerContent from '../../../components/ui/page-container/page-container-content.component';
import Dialog from '../../../components/ui/dialog';
+import {
+ ETH_GAS_PRICE_FETCH_WARNING_KEY,
+ GAS_PRICE_FETCH_FAILURE_ERROR_KEY,
+ GAS_PRICE_EXCESSIVE_ERROR_KEY,
+} from '../../../helpers/constants/error-keys';
import SendAmountRow from './send-amount-row';
import SendGasRow from './send-gas-row';
import SendHexDataRow from './send-hex-data-row';
@@ -21,16 +26,30 @@ export default class SendContent extends Component {
warning: PropTypes.string,
error: PropTypes.string,
gasIsExcessive: PropTypes.bool.isRequired,
+ isEthGasPrice: PropTypes.bool,
+ noGasPrice: PropTypes.bool,
};
updateGas = (updateData) => this.props.updateGas(updateData);
render() {
- const { warning, error, gasIsExcessive } = this.props;
+ const {
+ warning,
+ error,
+ gasIsExcessive,
+ isEthGasPrice,
+ noGasPrice,
+ } = this.props;
+
+ let gasError;
+ if (gasIsExcessive) gasError = GAS_PRICE_EXCESSIVE_ERROR_KEY;
+ else if (noGasPrice) gasError = GAS_PRICE_FETCH_FAILURE_ERROR_KEY;
+
return (
- {gasIsExcessive && this.renderError(true)}
+ {gasError && this.renderError(gasError)}
+ {isEthGasPrice && this.renderWarning(ETH_GAS_PRICE_FETCH_WARNING_KEY)}
{error && this.renderError()}
{warning && this.renderWarning()}
{this.maybeRenderAddContact()}
@@ -68,24 +87,22 @@ export default class SendContent extends Component {
);
}
- renderWarning() {
+ renderWarning(gasWarning = '') {
const { t } = this.context;
const { warning } = this.props;
-
return (
);
}
- renderError(gasError = false) {
+ renderError(gasError = '') {
const { t } = this.context;
const { error } = this.props;
-
return (
);
}
diff --git a/ui/app/pages/send/send-content/send-content.container.js b/ui/app/pages/send/send-content/send-content.container.js
index 42e115b94..3c99b3237 100644
--- a/ui/app/pages/send/send-content/send-content.container.js
+++ b/ui/app/pages/send/send-content/send-content.container.js
@@ -3,6 +3,8 @@ import {
getSendTo,
accountsWithSendEtherInfoSelector,
getAddressBookEntry,
+ getIsEthGasPriceFetched,
+ getNoGasPriceFetched,
} from '../../../selectors';
import * as actions from '../../../store/actions';
@@ -19,6 +21,8 @@ function mapStateToProps(state) {
),
contact: getAddressBookEntry(state, to),
to,
+ isEthGasPrice: getIsEthGasPriceFetched(state),
+ noGasPrice: getNoGasPriceFetched(state),
};
}
diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
index cf74ee0f3..9cbe21629 100644
--- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
+++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
@@ -26,6 +26,8 @@ export default class SendGasRow extends Component {
gasLimit: PropTypes.string,
insufficientBalance: PropTypes.bool,
isMainnet: PropTypes.bool,
+ isEthGasPrice: PropTypes.bool,
+ noGasPrice: PropTypes.bool,
};
static contextTypes = {
@@ -35,11 +37,19 @@ export default class SendGasRow extends Component {
renderAdvancedOptionsButton() {
const { metricsEvent } = this.context;
- const { showCustomizeGasModal, isMainnet } = this.props;
+ const {
+ showCustomizeGasModal,
+ isMainnet,
+ isEthGasPrice,
+ noGasPrice,
+ } = this.props;
// Tests should behave in same way as mainnet, but are using Localhost
if (!isMainnet && !process.env.IN_TEST) {
return null;
}
+ if (isEthGasPrice || noGasPrice) {
+ return null;
+ }
return (
@@ -148,7 +161,11 @@ export default class SendGasRow extends Component {
);
// Tests should behave in same way as mainnet, but are using Localhost
- if (advancedInlineGasShown || (!isMainnet && !process.env.IN_TEST)) {
+ if (
+ advancedInlineGasShown ||
+ (!isMainnet && !process.env.IN_TEST) ||
+ gasPriceFetchFailure
+ ) {
return advancedGasInputs;
} else if (gasButtonGroupShown) {
return gasPriceButtonGroup;
diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
index 210819f07..32b0529c4 100644
--- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
+++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
@@ -18,6 +18,8 @@ import {
getRenderableEstimateDataForSmallButtonsFromGWEI,
getDefaultActiveButtonIndex,
getIsMainnet,
+ getIsEthGasPriceFetched,
+ getNoGasPriceFetched,
} from '../../../../selectors';
import { isBalanceSufficient, calcGasTotal } from '../../send.utils';
import { calcMaxAmount } from '../send-amount-row/amount-max-button/amount-max-button.utils';
@@ -64,6 +66,8 @@ function mapStateToProps(state) {
balance,
conversionRate,
});
+ const isEthGasPrice = getIsEthGasPriceFetched(state);
+ const noGasPrice = getNoGasPriceFetched(state);
return {
balance: getSendFromBalance(state),
@@ -85,6 +89,8 @@ function mapStateToProps(state) {
sendToken: getSendToken(state),
tokenBalance: getTokenBalance(state),
isMainnet: getIsMainnet(state),
+ isEthGasPrice,
+ noGasPrice,
};
}
diff --git a/ui/app/pages/send/send-footer/send-footer.component.js b/ui/app/pages/send/send-footer/send-footer.component.js
index 5956d9517..ef18f48b1 100644
--- a/ui/app/pages/send/send-footer/send-footer.component.js
+++ b/ui/app/pages/send/send-footer/send-footer.component.js
@@ -27,6 +27,7 @@ export default class SendFooter extends Component {
gasEstimateType: PropTypes.string,
gasIsLoading: PropTypes.bool,
mostRecentOverviewPage: PropTypes.string.isRequired,
+ noGasPrice: PropTypes.bool,
};
static contextTypes = {
@@ -109,6 +110,7 @@ export default class SendFooter extends Component {
to,
gasLimit,
gasIsLoading,
+ noGasPrice,
} = this.props;
const missingTokenBalance = sendToken && !tokenBalance;
const gasLimitTooLow = gasLimit < 5208; // 5208 is hex value of 21000, minimum gas limit
@@ -118,7 +120,8 @@ export default class SendFooter extends Component {
missingTokenBalance ||
!(data || to) ||
gasLimitTooLow ||
- gasIsLoading;
+ gasIsLoading ||
+ noGasPrice;
return shouldBeDisabled;
}
diff --git a/ui/app/pages/send/send-footer/send-footer.component.test.js b/ui/app/pages/send/send-footer/send-footer.component.test.js
index cdcc06308..900c26b2a 100644
--- a/ui/app/pages/send/send-footer/send-footer.component.test.js
+++ b/ui/app/pages/send/send-footer/send-footer.component.test.js
@@ -49,6 +49,7 @@ describe('SendFooter Component', () => {
update={propsMethodSpies.update}
sendErrors={{}}
mostRecentOverviewPage="mostRecentOverviewPage"
+ noGasPrice={false}
/>,
{ context: { t: (str) => str, metricsEvent: () => ({}) } },
);
diff --git a/ui/app/pages/send/send-footer/send-footer.container.js b/ui/app/pages/send/send-footer/send-footer.container.js
index ff7b475bc..d68d16b60 100644
--- a/ui/app/pages/send/send-footer/send-footer.container.js
+++ b/ui/app/pages/send/send-footer/send-footer.container.js
@@ -24,6 +24,7 @@ import {
getGasIsLoading,
getRenderableEstimateDataForSmallButtonsFromGWEI,
getDefaultActiveButtonIndex,
+ getNoGasPriceFetched,
} from '../../../selectors';
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { addHexPrefix } from '../../../../../app/scripts/lib/util';
@@ -67,6 +68,7 @@ function mapStateToProps(state) {
gasEstimateType,
gasIsLoading: getGasIsLoading(state),
mostRecentOverviewPage: getMostRecentOverviewPage(state),
+ noGasPrice: getNoGasPriceFetched(state),
};
}
diff --git a/ui/app/selectors/custom-gas.js b/ui/app/selectors/custom-gas.js
index c84c48c71..5f01e8d08 100644
--- a/ui/app/selectors/custom-gas.js
+++ b/ui/app/selectors/custom-gas.js
@@ -27,12 +27,14 @@ export function getCustomGasPrice(state) {
}
export function getBasicGasEstimateLoadingStatus(state) {
- return state.gas.basicEstimateIsLoading;
+ return state.gas.basicEstimateStatus === 'LOADING';
}
export function getAveragePriceEstimateInHexWEI(state) {
- const averagePriceEstimate = state.gas.basicEstimates.average;
- return getGasPriceInHexWei(averagePriceEstimate || '0x0');
+ const averagePriceEstimate = state.gas.basicEstimates
+ ? state.gas.basicEstimates.average
+ : '0x0';
+ return getGasPriceInHexWei(averagePriceEstimate);
}
export function getFastPriceEstimateInHexWEI(state) {
@@ -355,3 +357,17 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) {
},
];
}
+
+export function getIsEthGasPriceFetched(state) {
+ const gasState = state.gas;
+ return Boolean(
+ gasState.estimateSource === 'eth_gasprice' &&
+ gasState.basicEstimateStatus === 'READY' &&
+ getIsMainnet(state),
+ );
+}
+
+export function getNoGasPriceFetched(state) {
+ const gasState = state.gas;
+ return Boolean(gasState.basicEstimateStatus === 'FAILED');
+}