diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js
index f44e5aa26..2bdef8a9a 100644
--- a/ui/components/app/edit-gas-display/edit-gas-display.component.js
+++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js
@@ -19,6 +19,7 @@ import {
import { areDappSuggestedAndTxParamGasFeesTheSame } from '../../../helpers/utils/confirm-tx.util';
import InfoTooltip from '../../ui/info-tooltip';
+import ErrorMessage from '../../ui/error-message';
import TransactionTotalBanner from '../transaction-total-banner/transaction-total-banner.component';
import RadioGroup from '../../ui/radio-group/radio-group.component';
import AdvancedGasControls from '../advanced-gas-controls/advanced-gas-controls.component';
@@ -59,6 +60,7 @@ export default function EditGasDisplay({
gasErrors,
onManualChange,
minimumGasLimit,
+ balanceError,
}) {
const t = useContext(I18nContext);
@@ -73,6 +75,12 @@ export default function EditGasDisplay({
);
const networkSupports1559 = useSelector(isEIP1559Network);
+ const showTopError = balanceError;
+
+ let errorKey;
+ if (balanceError) {
+ errorKey = 'insufficientFunds';
+ }
return (
@@ -85,6 +93,11 @@ export default function EditGasDisplay({
/>
)}
+ {showTopError && (
+
+
+
+ )}
{requireDappAcknowledgement && (
setShowEducationContent(false) : undefined
}
@@ -191,7 +192,7 @@ export default function EditGasPopover({
@@ -238,6 +239,7 @@ export default function EditGasPopover({
gasErrors={gasErrors}
onManualChange={onManualChange}
minimumGasLimit={minimumGasLimitDec}
+ balanceError={balanceError}
{...editGasDisplayProps}
/>
>
diff --git a/ui/components/app/edit-gas-popover/index.scss b/ui/components/app/edit-gas-popover/index.scss
new file mode 100644
index 000000000..c34c68fb7
--- /dev/null
+++ b/ui/components/app/edit-gas-popover/index.scss
@@ -0,0 +1,7 @@
+.edit-gas-popover {
+ &__wrapper {
+ @media screen and (min-width: 576px) {
+ max-height: 84vh;
+ }
+ }
+}
diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js
index b56303e74..0312a2123 100644
--- a/ui/hooks/useGasFeeInputs.js
+++ b/ui/hooks/useGasFeeInputs.js
@@ -11,6 +11,7 @@ import {
import {
multiplyCurrencies,
conversionLessThan,
+ conversionGreaterThan,
} from '../../shared/modules/conversion.utils';
import {
getMaximumGasTotalInHexWei,
@@ -22,9 +23,14 @@ import {
decGWEIToHexWEI,
decimalToHex,
hexToDecimal,
+ addHexes,
} from '../helpers/utils/conversions.util';
-import { getShouldShowFiat } from '../selectors';
import { GAS_FORM_ERRORS } from '../helpers/constants/gas';
+import {
+ getShouldShowFiat,
+ getSelectedAccount,
+ txDataSelector,
+} from '../selectors';
import { useCurrencyDisplay } from './useCurrencyDisplay';
import { useGasFeeEstimates } from './useGasFeeEstimates';
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency';
@@ -165,6 +171,9 @@ export function useGasFeeInputs(
minimumGasLimit,
editGasMode,
) {
+ const { balance: ethBalance } = useSelector(getSelectedAccount);
+ const txData = useSelector(txDataSelector);
+
// We need to know whether to show fiat conversions or not, so that we can
// default our fiat values to empty strings if showing fiat is not wanted or
// possible.
@@ -413,6 +422,16 @@ export function useGasFeeInputs(
...gasWarnings,
};
+ const minimumTxCostInHexWei = addHexes(
+ minimumCostInHexWei,
+ txData?.txParams?.value,
+ );
+
+ const balanceError = conversionGreaterThan(
+ { value: minimumTxCostInHexWei, fromNumericBase: 'hex' },
+ { value: ethBalance, fromNumericBase: 'hex' },
+ );
+
return {
maxFeePerGas: maxFeePerGasToUse,
maxFeePerGasFiat: showFiat ? maxFeePerGasFiat : '',
@@ -443,5 +462,6 @@ export function useGasFeeInputs(
setMaxFeePerGas(maxFeePerGasToUse);
setMaxPriorityFeePerGas(maxPriorityFeePerGasToUse);
},
+ balanceError,
};
}
diff --git a/ui/hooks/useGasFeeInputs.test.js b/ui/hooks/useGasFeeInputs.test.js
index a4b1400bb..96353be8f 100644
--- a/ui/hooks/useGasFeeInputs.test.js
+++ b/ui/hooks/useGasFeeInputs.test.js
@@ -7,7 +7,12 @@ import {
getNativeCurrency,
} from '../ducks/metamask/metamask';
import { ETH, PRIMARY } from '../helpers/constants/common';
-import { getCurrentCurrency, getShouldShowFiat } from '../selectors';
+import {
+ getCurrentCurrency,
+ getShouldShowFiat,
+ txDataSelector,
+ getSelectedAccount,
+} from '../selectors';
import { useGasFeeEstimates } from './useGasFeeEstimates';
import { useGasFeeInputs } from './useGasFeeInputs';
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency';
@@ -71,6 +76,32 @@ const FEE_MARKET_ESTIMATE_RETURN_VALUE = {
estimatedGasFeeTimeBounds: {},
};
+const HIGH_FEE_MARKET_ESTIMATE_RETURN_VALUE = {
+ gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
+ gasFeeEstimates: {
+ low: {
+ minWaitTimeEstimate: 180000,
+ maxWaitTimeEstimate: 300000,
+ suggestedMaxPriorityFeePerGas: '3',
+ suggestedMaxFeePerGas: '53000',
+ },
+ medium: {
+ minWaitTimeEstimate: 15000,
+ maxWaitTimeEstimate: 60000,
+ suggestedMaxPriorityFeePerGas: '7',
+ suggestedMaxFeePerGas: '70000',
+ },
+ high: {
+ minWaitTimeEstimate: 0,
+ maxWaitTimeEstimate: 15000,
+ suggestedMaxPriorityFeePerGas: '10',
+ suggestedMaxFeePerGas: '100000',
+ },
+ estimatedBaseFee: '50000',
+ },
+ estimatedGasFeeTimeBounds: {},
+};
+
const generateUseSelectorRouter = () => (selector) => {
if (selector === getConversionRate) {
return MOCK_ETH_USD_CONVERSION_RATE;
@@ -84,6 +115,18 @@ const generateUseSelectorRouter = () => (selector) => {
if (selector === getShouldShowFiat) {
return true;
}
+ if (selector === txDataSelector) {
+ return {
+ txParams: {
+ value: '0x5555',
+ },
+ };
+ }
+ if (selector === getSelectedAccount) {
+ return {
+ balance: '0x440aa47cc2556',
+ };
+ }
return undefined;
};
@@ -234,4 +277,32 @@ describe('useGasFeeInputs', () => {
// expect(result.current.estimatedMinimumFiat).toBe(`$${totalMaxFiat}`);
});
});
+
+ describe('when balance is sufficient for minimum transaction cost', () => {
+ beforeEach(() => {
+ useGasFeeEstimates.mockImplementation(
+ () => FEE_MARKET_ESTIMATE_RETURN_VALUE,
+ );
+ useSelector.mockImplementation(generateUseSelectorRouter());
+ });
+
+ it('should return false', () => {
+ const { result } = renderHook(() => useGasFeeInputs());
+ expect(result.current.balanceError).toBe(false);
+ });
+ });
+
+ describe('when balance is insufficient for minimum transaction cost', () => {
+ beforeEach(() => {
+ useGasFeeEstimates.mockImplementation(
+ () => HIGH_FEE_MARKET_ESTIMATE_RETURN_VALUE,
+ );
+ useSelector.mockImplementation(generateUseSelectorRouter());
+ });
+
+ it('should return true', () => {
+ const { result } = renderHook(() => useGasFeeInputs());
+ expect(result.current.balanceError).toBe(true);
+ });
+ });
});