Skip to content

Commit

Permalink
Handling gas price fetch failure (#10767)
Browse files Browse the repository at this point in the history
  • Loading branch information
NiranjanaBinoy authored and danjm committed Jun 6, 2021
1 parent d23db62 commit ffbd8e2
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 94 deletions.
6 changes: 6 additions & 0 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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."
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -81,11 +82,15 @@ export default class ConfirmPageContainerContent extends Component {
unapprovedTxCount,
rejectNText,
origin,
ethGasPriceWarning,
} = this.props;

return (
<div className="confirm-page-container-content">
{warning && <ConfirmPageContainerWarning warning={warning} />}
{ethGasPriceWarning && (
<ConfirmPageContainerWarning warning={ethGasPriceWarning} />
)}
<ConfirmPageContainerSummary
className={classnames({
'confirm-page-container-summary--border':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.confirm-page-container-warning {
background-color: #fffcdb;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid $geyser;
padding: 12px 24px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default class ConfirmPageContainer extends Component {
warning: PropTypes.string,
unapprovedTxCount: PropTypes.number,
origin: PropTypes.string.isRequired,
ethGasPriceWarning: PropTypes.string,
// Navigation
totalTx: PropTypes.number,
positionOfCurrentTx: PropTypes.number,
Expand Down Expand Up @@ -103,6 +104,7 @@ export default class ConfirmPageContainer extends Component {
hideSenderToRecipient,
showAccountInHeader,
origin,
ethGasPriceWarning,
} = this.props;
const renderAssetImage = contentComponent || !identiconAddress;

Expand Down Expand Up @@ -162,6 +164,7 @@ export default class ConfirmPageContainer extends Component {
unapprovedTxCount={unapprovedTxCount}
rejectNText={this.context.t('rejectTxsN', [unapprovedTxCount])}
origin={origin}
ethGasPriceWarning={ethGasPriceWarning}
/>
)}
{contentComponent && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
61 changes: 31 additions & 30 deletions ui/app/ducks/gas/gas-duck.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import sinon from 'sinon';
import BN from 'bn.js';

import GasReducer, {
basicGasEstimatesLoadingStarted,
basicGasEstimatesLoadingFinished,
setBasicEstimateStatus,
setBasicGasEstimateData,
setCustomGasPrice,
setCustomGasLimit,
Expand Down Expand Up @@ -49,7 +48,8 @@ describe('Gas Duck', () => {
fast: null,
safeLow: null,
},
basicEstimateIsLoading: true,
basicEstimateStatus: 'LOADING',
estimateSource: '',
};

const providerState = {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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',
});
});
});
Expand All @@ -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(
Expand All @@ -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' },
]);
});

Expand All @@ -190,18 +188,21 @@ 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: {
average: 0.0482,
},
},
]);
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' },
]);
});
});
Expand Down
78 changes: 49 additions & 29 deletions ui/app/ducks/gas/gas.duck.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
};
}

Expand All @@ -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;
};
}
Expand Down Expand Up @@ -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,
};
}
3 changes: 3 additions & 0 deletions ui/app/helpers/constants/error-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Loading

0 comments on commit ffbd8e2

Please sign in to comment.