mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Handling gas price fetch failure (#10767)
This commit is contained in:
parent
b73f543b23
commit
f1fc51667a
@ -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."
|
||||
},
|
||||
|
@ -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 (
|
||||
<div className="confirm-page-container-content">
|
||||
{warning && <ConfirmPageContainerWarning warning={warning} />}
|
||||
{ethGasPriceWarning && (
|
||||
<ConfirmPageContainerWarning warning={ethGasPriceWarning} />
|
||||
)}
|
||||
<ConfirmPageContainerSummary
|
||||
className={classnames({
|
||||
'confirm-page-container-summary--border':
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
@ -103,6 +104,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
hideSenderToRecipient,
|
||||
showAccountInHeader,
|
||||
origin,
|
||||
ethGasPriceWarning,
|
||||
} = this.props;
|
||||
const renderAssetImage = contentComponent || !identiconAddress;
|
||||
|
||||
@ -162,6 +164,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
unapprovedTxCount={unapprovedTxCount}
|
||||
rejectNText={this.context.t('rejectTxsN', [unapprovedTxCount])}
|
||||
origin={origin}
|
||||
ethGasPriceWarning={ethGasPriceWarning}
|
||||
/>
|
||||
)}
|
||||
{contentComponent && (
|
||||
|
@ -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 (
|
||||
|
@ -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' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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 ? (
|
||||
<Loading />
|
||||
@ -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 */
|
||||
|
@ -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 (
|
||||
<div className="confirm-page-container-content__details">
|
||||
@ -253,18 +284,26 @@ export default class ConfirmTransactionBase extends Component {
|
||||
<ConfirmDetailRow
|
||||
label="Gas Fee"
|
||||
value={hexTransactionFee}
|
||||
headerText={notMainnetOrTest ? '' : 'Edit'}
|
||||
headerText={notMainnetOrTest || gasPriceFetchFailure ? '' : 'Edit'}
|
||||
headerTextClassName={
|
||||
notMainnetOrTest ? '' : 'confirm-detail-row__header-text--edit'
|
||||
notMainnetOrTest || gasPriceFetchFailure
|
||||
? ''
|
||||
: 'confirm-detail-row__header-text--edit'
|
||||
}
|
||||
onHeaderClick={
|
||||
notMainnetOrTest || gasPriceFetchFailure
|
||||
? null
|
||||
: () => this.handleEditGas()
|
||||
}
|
||||
onHeaderClick={notMainnetOrTest ? null : () => this.handleEditGas()}
|
||||
secondaryText={
|
||||
hideFiatConversion
|
||||
? this.context.t('noConversionRateAvailable')
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
{advancedInlineGasShown || notMainnetOrTest ? (
|
||||
{advancedInlineGasShown ||
|
||||
notMainnetOrTest ||
|
||||
gasPriceFetchFailure ? (
|
||||
<AdvancedGasInputs
|
||||
updateCustomGasPrice={(newGasPrice) =>
|
||||
updateGasAndCalculate({ ...customGas, gasPrice: newGasPrice })
|
||||
@ -279,6 +318,11 @@ export default class ConfirmTransactionBase extends Component {
|
||||
isSpeedUp={false}
|
||||
/>
|
||||
) : null}
|
||||
{noGasPrice ? (
|
||||
<div className="confirm-page-container-content__error-container">
|
||||
<ErrorMessage errorKey={GAS_PRICE_FETCH_FAILURE_ERROR_KEY} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
@ -672,7 +716,12 @@ export default class ConfirmTransactionBase extends Component {
|
||||
showAccountInHeader,
|
||||
txData,
|
||||
} = this.props;
|
||||
const { submitting, submitError, submitWarning } = this.state;
|
||||
const {
|
||||
submitting,
|
||||
submitError,
|
||||
submitWarning,
|
||||
ethGasPriceWarning,
|
||||
} = this.state;
|
||||
|
||||
const { name } = methodData;
|
||||
const { valid, errorKey } = this.getErrorKey();
|
||||
@ -696,7 +745,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
functionType = t('contractInteraction');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmPageContainer
|
||||
fromName={fromName}
|
||||
@ -739,6 +787,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
onSubmit={() => this.handleSubmit()}
|
||||
hideSenderToRecipient={hideSenderToRecipient}
|
||||
origin={txData.origin}
|
||||
ethGasPriceWarning={ethGasPriceWarning}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 (
|
||||
<PageContainerContent>
|
||||
<div className="send-v2__form">
|
||||
{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 (
|
||||
<Dialog type="warning" className="send__error-dialog">
|
||||
{t(warning)}
|
||||
{gasWarning === '' ? t(warning) : t(gasWarning)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
renderError(gasError = false) {
|
||||
renderError(gasError = '') {
|
||||
const { t } = this.context;
|
||||
const { error } = this.props;
|
||||
|
||||
return (
|
||||
<Dialog type="error" className="send__error-dialog">
|
||||
{gasError ? t('gasPriceExcessive') : t(error)}
|
||||
{gasError === '' ? t(error) : t(gasError)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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 (
|
||||
<div
|
||||
className="advanced-gas-options-btn"
|
||||
@ -92,8 +102,11 @@ export default class SendGasRow extends Component {
|
||||
gasLimit,
|
||||
insufficientBalance,
|
||||
isMainnet,
|
||||
isEthGasPrice,
|
||||
noGasPrice,
|
||||
} = this.props;
|
||||
const { metricsEvent } = this.context;
|
||||
const gasPriceFetchFailure = isEthGasPrice || noGasPrice;
|
||||
|
||||
const gasPriceButtonGroup = (
|
||||
<div>
|
||||
@ -148,7 +161,11 @@ export default class SendGasRow extends Component {
|
||||
</div>
|
||||
);
|
||||
// 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;
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ describe('SendFooter Component', () => {
|
||||
update={propsMethodSpies.update}
|
||||
sendErrors={{}}
|
||||
mostRecentOverviewPage="mostRecentOverviewPage"
|
||||
noGasPrice={false}
|
||||
/>,
|
||||
{ context: { t: (str) => str, metricsEvent: () => ({}) } },
|
||||
);
|
||||
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user