mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +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": {
|
||||
"message": "Gas Price (GWEI)"
|
||||
},
|
||||
"gasPriceExcessive": {
|
||||
"message": "Your gas fee is set unnecessarily high. Consider lowering the amount."
|
||||
},
|
||||
"gasPriceExcessiveInput": {
|
||||
"message": "Gas Price Is Excessive"
|
||||
},
|
||||
"gasPriceExtremelyLow": {
|
||||
"message": "Gas Price Extremely Low"
|
||||
},
|
||||
|
@ -20,10 +20,12 @@ export default class AdvancedGasInputs extends Component {
|
||||
isSpeedUp: PropTypes.bool,
|
||||
customGasLimitMessage: PropTypes.string,
|
||||
minimumGasLimit: PropTypes.number,
|
||||
customPriceIsExcessive: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
minimumGasLimit: Number(MIN_GAS_LIMIT_DEC),
|
||||
customPriceIsExcessive: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -75,6 +77,7 @@ export default class AdvancedGasInputs extends Component {
|
||||
customPriceIsSafe,
|
||||
isSpeedUp,
|
||||
gasPrice,
|
||||
customPriceIsExcessive,
|
||||
}) {
|
||||
const { t } = this.context;
|
||||
|
||||
@ -93,6 +96,11 @@ export default class AdvancedGasInputs extends Component {
|
||||
errorText: t('gasPriceExtremelyLow'),
|
||||
errorType: 'warning',
|
||||
};
|
||||
} else if (customPriceIsExcessive) {
|
||||
return {
|
||||
errorText: t('gasPriceExcessiveInput'),
|
||||
errorType: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
@ -185,6 +193,7 @@ export default class AdvancedGasInputs extends Component {
|
||||
isSpeedUp,
|
||||
customGasLimitMessage,
|
||||
minimumGasLimit,
|
||||
customPriceIsExcessive,
|
||||
} = this.props;
|
||||
const { gasPrice, gasLimit } = this.state;
|
||||
|
||||
@ -196,6 +205,7 @@ export default class AdvancedGasInputs extends Component {
|
||||
customPriceIsSafe,
|
||||
isSpeedUp,
|
||||
gasPrice,
|
||||
customPriceIsExcessive,
|
||||
});
|
||||
const gasPriceErrorComponent = gasPriceErrorType ? (
|
||||
<div
|
||||
|
@ -108,4 +108,15 @@ describe('Advanced Gas Inputs', function () {
|
||||
|
||||
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,
|
||||
customGasLimitMessage: PropTypes.string,
|
||||
minimumGasLimit: PropTypes.number,
|
||||
customPriceIsExcessive: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
renderDataSummary(transactionFee) {
|
||||
@ -47,6 +48,7 @@ export default class AdvancedTabContent extends Component {
|
||||
transactionFee,
|
||||
customGasLimitMessage,
|
||||
minimumGasLimit,
|
||||
customPriceIsExcessive,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -64,6 +66,7 @@ export default class AdvancedTabContent extends Component {
|
||||
isSpeedUp={isSpeedUp}
|
||||
customGasLimitMessage={customGasLimitMessage}
|
||||
minimumGasLimit={minimumGasLimit}
|
||||
customPriceIsExcessive={customPriceIsExcessive}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -35,6 +35,7 @@ export default class GasModalPageContainer extends Component {
|
||||
isSpeedUp: PropTypes.bool,
|
||||
isRetry: PropTypes.bool,
|
||||
disableSave: PropTypes.bool,
|
||||
customPriceIsExcessive: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -57,6 +58,7 @@ export default class GasModalPageContainer extends Component {
|
||||
customPriceIsSafe,
|
||||
isSpeedUp,
|
||||
isRetry,
|
||||
customPriceIsExcessive,
|
||||
infoRowProps: { transactionFee },
|
||||
} = this.props;
|
||||
|
||||
@ -71,6 +73,7 @@ export default class GasModalPageContainer extends Component {
|
||||
customPriceIsSafe={customPriceIsSafe}
|
||||
isSpeedUp={isSpeedUp}
|
||||
isRetry={isRetry}
|
||||
customPriceIsExcessive={customPriceIsExcessive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
getTokenBalance,
|
||||
getSendMaxModeState,
|
||||
getAveragePriceEstimateInHexWEI,
|
||||
isCustomPriceExcessive,
|
||||
} from '../../../../selectors';
|
||||
|
||||
import {
|
||||
@ -141,6 +142,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
customGasTotal,
|
||||
newTotalFiat,
|
||||
customPriceIsSafe: isCustomPriceSafe(state),
|
||||
customPriceIsExcessive: isCustomPriceExcessive(state),
|
||||
maxModeOn,
|
||||
gasPriceButtonGroupProps: {
|
||||
buttonDataLoading,
|
||||
|
@ -126,6 +126,7 @@ describe('gas-modal-page-container container', function () {
|
||||
conversionRate: 50,
|
||||
customModalGasLimitInHex: 'aaaaaaaa',
|
||||
customModalGasPriceInHex: 'ffffffff',
|
||||
customPriceIsExcessive: false,
|
||||
customGasTotal: 'aaaaaaa955555556',
|
||||
customPriceIsSafe: true,
|
||||
gasPriceButtonGroupProps: {
|
||||
|
@ -20,15 +20,17 @@ export default class SendContent extends Component {
|
||||
isOwnedAccount: PropTypes.bool,
|
||||
warning: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
gasIsExcessive: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
updateGas = (updateData) => this.props.updateGas(updateData);
|
||||
|
||||
render() {
|
||||
const { warning, error } = this.props;
|
||||
const { warning, error, gasIsExcessive } = this.props;
|
||||
return (
|
||||
<PageContainerContent>
|
||||
<div className="send-v2__form">
|
||||
{gasIsExcessive && this.renderError(true)}
|
||||
{error && this.renderError()}
|
||||
{warning && this.renderWarning()}
|
||||
{this.maybeRenderAddContact()}
|
||||
@ -77,13 +79,13 @@ export default class SendContent extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderError() {
|
||||
renderError(gasError = false) {
|
||||
const { t } = this.context;
|
||||
const { error } = this.props;
|
||||
|
||||
return (
|
||||
<Dialog type="error" className="send__error-dialog">
|
||||
{t(error)}
|
||||
{gasError ? t('gasPriceExcessive') : t(error)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ export default class SendTransactionScreen extends Component {
|
||||
qrCodeDetected: PropTypes.func.isRequired,
|
||||
qrCodeData: PropTypes.object,
|
||||
sendTokenAddress: PropTypes.string,
|
||||
gasIsExcessive: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -382,7 +383,7 @@ export default class SendTransactionScreen extends Component {
|
||||
}
|
||||
|
||||
renderSendContent() {
|
||||
const { history, showHexData } = this.props;
|
||||
const { history, showHexData, gasIsExcessive } = this.props;
|
||||
const { toWarning, toError } = this.state;
|
||||
|
||||
return [
|
||||
@ -394,6 +395,7 @@ export default class SendTransactionScreen extends Component {
|
||||
showHexData={showHexData}
|
||||
warning={toWarning}
|
||||
error={toError}
|
||||
gasIsExcessive={gasIsExcessive}
|
||||
/>,
|
||||
<SendFooter key="send-footer" history={history} />,
|
||||
];
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
getSelectedAddress,
|
||||
getAddressBook,
|
||||
getSendTokenAddress,
|
||||
isCustomPriceExcessive,
|
||||
} from '../../selectors';
|
||||
|
||||
import {
|
||||
@ -67,6 +68,7 @@ function mapStateToProps(state) {
|
||||
tokenBalance: getTokenBalance(state),
|
||||
tokenContract: getSendTokenContract(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 { 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;
|
||||
|
||||
@ -31,7 +36,7 @@ export function getAveragePriceEstimateInHexWEI(state) {
|
||||
}
|
||||
|
||||
export function getFastPriceEstimateInHexWEI(state) {
|
||||
const fastPriceEstimate = state.gas.basicEstimates.fast;
|
||||
const fastPriceEstimate = getFastPriceEstimate(state);
|
||||
return getGasPriceInHexWei(fastPriceEstimate || '0x0');
|
||||
}
|
||||
|
||||
@ -55,6 +60,16 @@ export function getSafeLowEstimate(state) {
|
||||
return safeLow;
|
||||
}
|
||||
|
||||
export function getFastPriceEstimate(state) {
|
||||
const {
|
||||
gas: {
|
||||
basicEstimates: { fast },
|
||||
},
|
||||
} = state;
|
||||
|
||||
return fast;
|
||||
}
|
||||
|
||||
export function isCustomPriceSafe(state) {
|
||||
const safeLow = getSafeLowEstimate(state);
|
||||
|
||||
@ -81,6 +96,31 @@ export function isCustomPriceSafe(state) {
|
||||
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(
|
||||
estimate,
|
||||
gasLimit,
|
||||
|
@ -7,6 +7,7 @@ const {
|
||||
getRenderableBasicEstimateData,
|
||||
getRenderableEstimateDataForSmallButtonsFromGWEI,
|
||||
isCustomPriceSafe,
|
||||
isCustomPriceExcessive,
|
||||
} = proxyquire('../custom-gas', {});
|
||||
|
||||
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 () {
|
||||
it('should return gas.customData.limit', function () {
|
||||
const mockState = { gas: { customData: { limit: 'mockLimit' } } };
|
||||
|
Loading…
Reference in New Issue
Block a user