mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Adding warnings for excessive custom gas input (#10582)
Fixes MetaMask/metamask-extension#9811
This commit is contained in:
parent
79a7199a2f
commit
45c076e232
@ -763,6 +763,12 @@
|
|||||||
"gasPrice": {
|
"gasPrice": {
|
||||||
"message": "Gas Price (GWEI)"
|
"message": "Gas Price (GWEI)"
|
||||||
},
|
},
|
||||||
|
"gasPriceExcessive": {
|
||||||
|
"message": "Your gas fee is set unnecessarily high. Consider lowering the amount."
|
||||||
|
},
|
||||||
|
"gasPriceExcessiveInput": {
|
||||||
|
"message": "Gas Price Is Excessive"
|
||||||
|
},
|
||||||
"gasPriceExtremelyLow": {
|
"gasPriceExtremelyLow": {
|
||||||
"message": "Gas Price Extremely Low"
|
"message": "Gas Price Extremely Low"
|
||||||
},
|
},
|
||||||
|
@ -20,10 +20,12 @@ export default class AdvancedGasInputs extends Component {
|
|||||||
isSpeedUp: PropTypes.bool,
|
isSpeedUp: PropTypes.bool,
|
||||||
customGasLimitMessage: PropTypes.string,
|
customGasLimitMessage: PropTypes.string,
|
||||||
minimumGasLimit: PropTypes.number,
|
minimumGasLimit: PropTypes.number,
|
||||||
|
customPriceIsExcessive: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
minimumGasLimit: Number(MIN_GAS_LIMIT_DEC),
|
minimumGasLimit: Number(MIN_GAS_LIMIT_DEC),
|
||||||
|
customPriceIsExcessive: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -75,6 +77,7 @@ export default class AdvancedGasInputs extends Component {
|
|||||||
customPriceIsSafe,
|
customPriceIsSafe,
|
||||||
isSpeedUp,
|
isSpeedUp,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
|
customPriceIsExcessive,
|
||||||
}) {
|
}) {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
|
|
||||||
@ -93,6 +96,11 @@ export default class AdvancedGasInputs extends Component {
|
|||||||
errorText: t('gasPriceExtremelyLow'),
|
errorText: t('gasPriceExtremelyLow'),
|
||||||
errorType: 'warning',
|
errorType: 'warning',
|
||||||
};
|
};
|
||||||
|
} else if (customPriceIsExcessive) {
|
||||||
|
return {
|
||||||
|
errorText: t('gasPriceExcessiveInput'),
|
||||||
|
errorType: 'error',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@ -185,6 +193,7 @@ export default class AdvancedGasInputs extends Component {
|
|||||||
isSpeedUp,
|
isSpeedUp,
|
||||||
customGasLimitMessage,
|
customGasLimitMessage,
|
||||||
minimumGasLimit,
|
minimumGasLimit,
|
||||||
|
customPriceIsExcessive,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { gasPrice, gasLimit } = this.state;
|
const { gasPrice, gasLimit } = this.state;
|
||||||
|
|
||||||
@ -196,6 +205,7 @@ export default class AdvancedGasInputs extends Component {
|
|||||||
customPriceIsSafe,
|
customPriceIsSafe,
|
||||||
isSpeedUp,
|
isSpeedUp,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
|
customPriceIsExcessive,
|
||||||
});
|
});
|
||||||
const gasPriceErrorComponent = gasPriceErrorType ? (
|
const gasPriceErrorComponent = gasPriceErrorType ? (
|
||||||
<div
|
<div
|
||||||
|
@ -108,4 +108,15 @@ describe('Advanced Gas Inputs', function () {
|
|||||||
|
|
||||||
assert.strictEqual(renderWarning.text(), 'gasPriceExtremelyLow');
|
assert.strictEqual(renderWarning.text(), 'gasPriceExtremelyLow');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors when custom gas price is too excessive', function () {
|
||||||
|
wrapper.setProps({ customPriceIsExcessive: true });
|
||||||
|
|
||||||
|
const renderError = wrapper.find(
|
||||||
|
'.advanced-gas-inputs__gas-edit-row__error-text',
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(renderError.length, 2);
|
||||||
|
assert.strictEqual(renderError.at(0).text(), 'gasPriceExcessiveInput');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,6 +18,7 @@ export default class AdvancedTabContent extends Component {
|
|||||||
isSpeedUp: PropTypes.bool,
|
isSpeedUp: PropTypes.bool,
|
||||||
customGasLimitMessage: PropTypes.string,
|
customGasLimitMessage: PropTypes.string,
|
||||||
minimumGasLimit: PropTypes.number,
|
minimumGasLimit: PropTypes.number,
|
||||||
|
customPriceIsExcessive: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDataSummary(transactionFee) {
|
renderDataSummary(transactionFee) {
|
||||||
@ -47,6 +48,7 @@ export default class AdvancedTabContent extends Component {
|
|||||||
transactionFee,
|
transactionFee,
|
||||||
customGasLimitMessage,
|
customGasLimitMessage,
|
||||||
minimumGasLimit,
|
minimumGasLimit,
|
||||||
|
customPriceIsExcessive,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -64,6 +66,7 @@ export default class AdvancedTabContent extends Component {
|
|||||||
isSpeedUp={isSpeedUp}
|
isSpeedUp={isSpeedUp}
|
||||||
customGasLimitMessage={customGasLimitMessage}
|
customGasLimitMessage={customGasLimitMessage}
|
||||||
minimumGasLimit={minimumGasLimit}
|
minimumGasLimit={minimumGasLimit}
|
||||||
|
customPriceIsExcessive={customPriceIsExcessive}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,6 +35,7 @@ export default class GasModalPageContainer extends Component {
|
|||||||
isSpeedUp: PropTypes.bool,
|
isSpeedUp: PropTypes.bool,
|
||||||
isRetry: PropTypes.bool,
|
isRetry: PropTypes.bool,
|
||||||
disableSave: PropTypes.bool,
|
disableSave: PropTypes.bool,
|
||||||
|
customPriceIsExcessive: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -57,6 +58,7 @@ export default class GasModalPageContainer extends Component {
|
|||||||
customPriceIsSafe,
|
customPriceIsSafe,
|
||||||
isSpeedUp,
|
isSpeedUp,
|
||||||
isRetry,
|
isRetry,
|
||||||
|
customPriceIsExcessive,
|
||||||
infoRowProps: { transactionFee },
|
infoRowProps: { transactionFee },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ export default class GasModalPageContainer extends Component {
|
|||||||
customPriceIsSafe={customPriceIsSafe}
|
customPriceIsSafe={customPriceIsSafe}
|
||||||
isSpeedUp={isSpeedUp}
|
isSpeedUp={isSpeedUp}
|
||||||
isRetry={isRetry}
|
isRetry={isRetry}
|
||||||
|
customPriceIsExcessive={customPriceIsExcessive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import {
|
|||||||
getTokenBalance,
|
getTokenBalance,
|
||||||
getSendMaxModeState,
|
getSendMaxModeState,
|
||||||
getAveragePriceEstimateInHexWEI,
|
getAveragePriceEstimateInHexWEI,
|
||||||
|
isCustomPriceExcessive,
|
||||||
} from '../../../../selectors';
|
} from '../../../../selectors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -141,6 +142,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
customGasTotal,
|
customGasTotal,
|
||||||
newTotalFiat,
|
newTotalFiat,
|
||||||
customPriceIsSafe: isCustomPriceSafe(state),
|
customPriceIsSafe: isCustomPriceSafe(state),
|
||||||
|
customPriceIsExcessive: isCustomPriceExcessive(state),
|
||||||
maxModeOn,
|
maxModeOn,
|
||||||
gasPriceButtonGroupProps: {
|
gasPriceButtonGroupProps: {
|
||||||
buttonDataLoading,
|
buttonDataLoading,
|
||||||
|
@ -126,6 +126,7 @@ describe('gas-modal-page-container container', function () {
|
|||||||
conversionRate: 50,
|
conversionRate: 50,
|
||||||
customModalGasLimitInHex: 'aaaaaaaa',
|
customModalGasLimitInHex: 'aaaaaaaa',
|
||||||
customModalGasPriceInHex: 'ffffffff',
|
customModalGasPriceInHex: 'ffffffff',
|
||||||
|
customPriceIsExcessive: false,
|
||||||
customGasTotal: 'aaaaaaa955555556',
|
customGasTotal: 'aaaaaaa955555556',
|
||||||
customPriceIsSafe: true,
|
customPriceIsSafe: true,
|
||||||
gasPriceButtonGroupProps: {
|
gasPriceButtonGroupProps: {
|
||||||
|
@ -20,15 +20,17 @@ export default class SendContent extends Component {
|
|||||||
isOwnedAccount: PropTypes.bool,
|
isOwnedAccount: PropTypes.bool,
|
||||||
warning: PropTypes.string,
|
warning: PropTypes.string,
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
|
gasIsExcessive: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
updateGas = (updateData) => this.props.updateGas(updateData);
|
updateGas = (updateData) => this.props.updateGas(updateData);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { warning, error } = this.props;
|
const { warning, error, gasIsExcessive } = this.props;
|
||||||
return (
|
return (
|
||||||
<PageContainerContent>
|
<PageContainerContent>
|
||||||
<div className="send-v2__form">
|
<div className="send-v2__form">
|
||||||
|
{gasIsExcessive && this.renderError(true)}
|
||||||
{error && this.renderError()}
|
{error && this.renderError()}
|
||||||
{warning && this.renderWarning()}
|
{warning && this.renderWarning()}
|
||||||
{this.maybeRenderAddContact()}
|
{this.maybeRenderAddContact()}
|
||||||
@ -77,13 +79,13 @@ export default class SendContent extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderError() {
|
renderError(gasError = false) {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
const { error } = this.props;
|
const { error } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog type="error" className="send__error-dialog">
|
<Dialog type="error" className="send__error-dialog">
|
||||||
{t(error)}
|
{gasError ? t('gasPriceExcessive') : t(error)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ export default class SendTransactionScreen extends Component {
|
|||||||
qrCodeDetected: PropTypes.func.isRequired,
|
qrCodeDetected: PropTypes.func.isRequired,
|
||||||
qrCodeData: PropTypes.object,
|
qrCodeData: PropTypes.object,
|
||||||
sendTokenAddress: PropTypes.string,
|
sendTokenAddress: PropTypes.string,
|
||||||
|
gasIsExcessive: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -382,7 +383,7 @@ export default class SendTransactionScreen extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSendContent() {
|
renderSendContent() {
|
||||||
const { history, showHexData } = this.props;
|
const { history, showHexData, gasIsExcessive } = this.props;
|
||||||
const { toWarning, toError } = this.state;
|
const { toWarning, toError } = this.state;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -394,6 +395,7 @@ export default class SendTransactionScreen extends Component {
|
|||||||
showHexData={showHexData}
|
showHexData={showHexData}
|
||||||
warning={toWarning}
|
warning={toWarning}
|
||||||
error={toError}
|
error={toError}
|
||||||
|
gasIsExcessive={gasIsExcessive}
|
||||||
/>,
|
/>,
|
||||||
<SendFooter key="send-footer" history={history} />,
|
<SendFooter key="send-footer" history={history} />,
|
||||||
];
|
];
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
getAddressBook,
|
getAddressBook,
|
||||||
getSendTokenAddress,
|
getSendTokenAddress,
|
||||||
|
isCustomPriceExcessive,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -67,6 +68,7 @@ function mapStateToProps(state) {
|
|||||||
tokenBalance: getTokenBalance(state),
|
tokenBalance: getTokenBalance(state),
|
||||||
tokenContract: getSendTokenContract(state),
|
tokenContract: getSendTokenContract(state),
|
||||||
sendTokenAddress: getSendTokenAddress(state),
|
sendTokenAddress: getSendTokenAddress(state),
|
||||||
|
gasIsExcessive: isCustomPriceExcessive(state, true),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,12 @@ import { formatETHFee } from '../helpers/utils/formatters';
|
|||||||
import { calcGasTotal } from '../pages/send/send.utils';
|
import { calcGasTotal } from '../pages/send/send.utils';
|
||||||
|
|
||||||
import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common';
|
import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common';
|
||||||
import { getCurrentCurrency, getIsMainnet, getPreferences } from '.';
|
import {
|
||||||
|
getCurrentCurrency,
|
||||||
|
getIsMainnet,
|
||||||
|
getPreferences,
|
||||||
|
getGasPrice,
|
||||||
|
} from '.';
|
||||||
|
|
||||||
const NUMBER_OF_DECIMALS_SM_BTNS = 5;
|
const NUMBER_OF_DECIMALS_SM_BTNS = 5;
|
||||||
|
|
||||||
@ -31,7 +36,7 @@ export function getAveragePriceEstimateInHexWEI(state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFastPriceEstimateInHexWEI(state) {
|
export function getFastPriceEstimateInHexWEI(state) {
|
||||||
const fastPriceEstimate = state.gas.basicEstimates.fast;
|
const fastPriceEstimate = getFastPriceEstimate(state);
|
||||||
return getGasPriceInHexWei(fastPriceEstimate || '0x0');
|
return getGasPriceInHexWei(fastPriceEstimate || '0x0');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +60,16 @@ export function getSafeLowEstimate(state) {
|
|||||||
return safeLow;
|
return safeLow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFastPriceEstimate(state) {
|
||||||
|
const {
|
||||||
|
gas: {
|
||||||
|
basicEstimates: { fast },
|
||||||
|
},
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return fast;
|
||||||
|
}
|
||||||
|
|
||||||
export function isCustomPriceSafe(state) {
|
export function isCustomPriceSafe(state) {
|
||||||
const safeLow = getSafeLowEstimate(state);
|
const safeLow = getSafeLowEstimate(state);
|
||||||
|
|
||||||
@ -81,6 +96,31 @@ export function isCustomPriceSafe(state) {
|
|||||||
return customPriceSafe;
|
return customPriceSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCustomPriceExcessive(state, checkSend = false) {
|
||||||
|
const customPrice = checkSend ? getGasPrice(state) : getCustomGasPrice(state);
|
||||||
|
const fastPrice = getFastPriceEstimate(state);
|
||||||
|
|
||||||
|
if (!customPrice || !fastPrice) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom gas should be considered excessive when it is 1.5 times greater than the fastest estimate.
|
||||||
|
const customPriceExcessive = conversionGreaterThan(
|
||||||
|
{
|
||||||
|
value: customPrice,
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
fromDenomination: 'WEI',
|
||||||
|
toDenomination: 'GWEI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
value: Math.floor(fastPrice * 1.5),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return customPriceExcessive;
|
||||||
|
}
|
||||||
|
|
||||||
export function basicPriceEstimateToETHTotal(
|
export function basicPriceEstimateToETHTotal(
|
||||||
estimate,
|
estimate,
|
||||||
gasLimit,
|
gasLimit,
|
||||||
|
@ -7,6 +7,7 @@ const {
|
|||||||
getRenderableBasicEstimateData,
|
getRenderableBasicEstimateData,
|
||||||
getRenderableEstimateDataForSmallButtonsFromGWEI,
|
getRenderableEstimateDataForSmallButtonsFromGWEI,
|
||||||
isCustomPriceSafe,
|
isCustomPriceSafe,
|
||||||
|
isCustomPriceExcessive,
|
||||||
} = proxyquire('../custom-gas', {});
|
} = proxyquire('../custom-gas', {});
|
||||||
|
|
||||||
describe('custom-gas selectors', function () {
|
describe('custom-gas selectors', function () {
|
||||||
@ -55,6 +56,91 @@ describe('custom-gas selectors', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isCustomPriceExcessive()', function () {
|
||||||
|
it('should return false for gas.customData.price null', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: null },
|
||||||
|
basicEstimates: { fast: 150 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), false);
|
||||||
|
});
|
||||||
|
it('should return false gas.basicEstimates.fast undefined', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: '0x77359400' },
|
||||||
|
basicEstimates: { fast: undefined },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), false);
|
||||||
|
});
|
||||||
|
it('should return false gas.basicEstimates.price 0x205d0bae00 (139)', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: '0x205d0bae00' },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), false);
|
||||||
|
});
|
||||||
|
it('should return false gas.basicEstimates.price 0x1bf08eb000 (120)', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: '0x1bf08eb000' },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), false);
|
||||||
|
});
|
||||||
|
it('should return false gas.basicEstimates.price 0x28bed01600 (175)', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: '0x28bed01600' },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), false);
|
||||||
|
});
|
||||||
|
it('should return true gas.basicEstimates.price 0x30e4f9b400 (210)', function () {
|
||||||
|
const mockState = {
|
||||||
|
gas: {
|
||||||
|
customData: { price: '0x30e4f9b400' },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState), true);
|
||||||
|
});
|
||||||
|
it('should return false gas.basicEstimates.price 0x28bed01600 (175) (checkSend=true)', function () {
|
||||||
|
const mockState = {
|
||||||
|
metamask: {
|
||||||
|
send: {
|
||||||
|
gasPrice: '0x28bed0160',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gas: {
|
||||||
|
customData: { price: null },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState, true), false);
|
||||||
|
});
|
||||||
|
it('should return true gas.basicEstimates.price 0x30e4f9b400 (210) (checkSend=true)', function () {
|
||||||
|
const mockState = {
|
||||||
|
metamask: {
|
||||||
|
send: {
|
||||||
|
gasPrice: '0x30e4f9b400',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gas: {
|
||||||
|
customData: { price: null },
|
||||||
|
basicEstimates: { fast: 139 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.strictEqual(isCustomPriceExcessive(mockState, true), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getCustomGasLimit()', function () {
|
describe('getCustomGasLimit()', function () {
|
||||||
it('should return gas.customData.limit', function () {
|
it('should return gas.customData.limit', function () {
|
||||||
const mockState = { gas: { customData: { limit: 'mockLimit' } } };
|
const mockState = { gas: { customData: { limit: 'mockLimit' } } };
|
||||||
|
Loading…
Reference in New Issue
Block a user