mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-23 10:30:04 +01:00
3732c5f71e
ESLint rules have been added to enforce our JSDoc conventions. These rules were introduced by updating `@metamask/eslint-config` to v9. Some of the rules have been disabled because the effort to fix all lint errors was too high. It might be easiest to enable these rules one directory at a time, or one rule at a time. Most of the changes in this PR were a result of running `yarn lint:fix`. There were a handful of manual changes that seemed obvious and simple to make. Anything beyond that and the rule was left disabled.
304 lines
7.7 KiB
JavaScript
304 lines
7.7 KiB
JavaScript
import { useMemo } from 'react';
|
|
import { shallowEqual, useSelector } from 'react-redux';
|
|
import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas';
|
|
import {
|
|
conversionLessThan,
|
|
conversionGreaterThan,
|
|
} from '../../../shared/modules/conversion.utils';
|
|
import {
|
|
checkNetworkAndAccountSupports1559,
|
|
getSelectedAccount,
|
|
} from '../../selectors';
|
|
import { addHexes } from '../../helpers/utils/conversions.util';
|
|
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
|
|
import {
|
|
bnGreaterThan,
|
|
bnLessThan,
|
|
bnLessThanEqualTo,
|
|
} from '../../helpers/utils/util';
|
|
import { GAS_FORM_ERRORS } from '../../helpers/constants/gas';
|
|
|
|
const HIGH_FEE_WARNING_MULTIPLIER = 1.5;
|
|
|
|
const validateGasLimit = (gasLimit, minimumGasLimit) => {
|
|
const gasLimitTooLow = conversionLessThan(
|
|
{ value: gasLimit, fromNumericBase: 'dec' },
|
|
{ value: minimumGasLimit || GAS_LIMITS.SIMPLE, fromNumericBase: 'hex' },
|
|
);
|
|
|
|
if (gasLimitTooLow) {
|
|
return GAS_FORM_ERRORS.GAS_LIMIT_OUT_OF_BOUNDS;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const validateMaxPriorityFee = (maxPriorityFeePerGas, supportsEIP1559) => {
|
|
if (!supportsEIP1559) {
|
|
return undefined;
|
|
}
|
|
if (bnLessThanEqualTo(maxPriorityFeePerGas, 0)) {
|
|
return GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const validateMaxFee = (
|
|
maxFeePerGas,
|
|
maxPriorityFeeError,
|
|
maxPriorityFeePerGas,
|
|
supportsEIP1559,
|
|
) => {
|
|
if (maxPriorityFeeError || !supportsEIP1559) {
|
|
return undefined;
|
|
}
|
|
if (bnGreaterThan(maxPriorityFeePerGas, maxFeePerGas)) {
|
|
return GAS_FORM_ERRORS.MAX_FEE_IMBALANCE;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const validateGasPrice = (
|
|
isFeeMarketGasEstimate,
|
|
gasPrice,
|
|
supportsEIP1559,
|
|
transaction,
|
|
) => {
|
|
if (supportsEIP1559 && isFeeMarketGasEstimate) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
(!supportsEIP1559 || transaction?.txParams?.gasPrice) &&
|
|
bnLessThanEqualTo(gasPrice, 0)
|
|
) {
|
|
return GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const getMaxPriorityFeeWarning = (
|
|
gasFeeEstimates,
|
|
isFeeMarketGasEstimate,
|
|
isGasEstimatesLoading,
|
|
maxPriorityFeePerGas,
|
|
supportsEIP1559,
|
|
) => {
|
|
if (!supportsEIP1559 || !isFeeMarketGasEstimate || isGasEstimatesLoading) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
bnLessThan(
|
|
maxPriorityFeePerGas,
|
|
gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas,
|
|
)
|
|
) {
|
|
return GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW;
|
|
}
|
|
if (
|
|
gasFeeEstimates?.high &&
|
|
bnGreaterThan(
|
|
maxPriorityFeePerGas,
|
|
gasFeeEstimates.high.suggestedMaxPriorityFeePerGas *
|
|
HIGH_FEE_WARNING_MULTIPLIER,
|
|
)
|
|
) {
|
|
return GAS_FORM_ERRORS.MAX_PRIORITY_FEE_HIGH_WARNING;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const getMaxFeeWarning = (
|
|
gasFeeEstimates,
|
|
isGasEstimatesLoading,
|
|
isFeeMarketGasEstimate,
|
|
maxFeeError,
|
|
maxPriorityFeeError,
|
|
maxFeePerGas,
|
|
supportsEIP1559,
|
|
) => {
|
|
if (
|
|
maxPriorityFeeError ||
|
|
maxFeeError ||
|
|
!isFeeMarketGasEstimate ||
|
|
!supportsEIP1559 ||
|
|
isGasEstimatesLoading
|
|
) {
|
|
return undefined;
|
|
}
|
|
if (bnLessThan(maxFeePerGas, gasFeeEstimates?.low?.suggestedMaxFeePerGas)) {
|
|
return GAS_FORM_ERRORS.MAX_FEE_TOO_LOW;
|
|
}
|
|
if (
|
|
gasFeeEstimates?.high &&
|
|
bnGreaterThan(
|
|
maxFeePerGas,
|
|
gasFeeEstimates.high.suggestedMaxFeePerGas * HIGH_FEE_WARNING_MULTIPLIER,
|
|
)
|
|
) {
|
|
return GAS_FORM_ERRORS.MAX_FEE_HIGH_WARNING;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const hasBalanceError = (minimumCostInHexWei, transaction, ethBalance) => {
|
|
if (minimumCostInHexWei === undefined || ethBalance === undefined) {
|
|
return false;
|
|
}
|
|
const minimumTxCostInHexWei = addHexes(
|
|
minimumCostInHexWei,
|
|
transaction?.txParams?.value || '0x0',
|
|
);
|
|
|
|
return conversionGreaterThan(
|
|
{ value: minimumTxCostInHexWei, fromNumericBase: 'hex' },
|
|
{ value: ethBalance, fromNumericBase: 'hex' },
|
|
);
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} GasFeeErrorsReturnType
|
|
* @property {Object} [gasErrors] - combined map of errors and warnings.
|
|
* @property {boolean} [hasGasErrors] - true if there are errors that can block submission.
|
|
* @property {Object} gasWarnings - map of gas warnings for EIP-1559 fields.
|
|
* @property {boolean} [balanceError] - true if user balance is less than transaction value.
|
|
* @property {boolean} [estimatesUnavailableWarning] - true if supportsEIP1559 is true and
|
|
* estimate is not of type fee-market.
|
|
*/
|
|
|
|
/**
|
|
* @param options
|
|
* @param options.gasEstimateType
|
|
* @param options.gasFeeEstimates
|
|
* @param options.isGasEstimatesLoading
|
|
* @param options.gasLimit
|
|
* @param options.gasPrice
|
|
* @param options.maxPriorityFeePerGas
|
|
* @param options.maxFeePerGas
|
|
* @param options.minimumCostInHexWei
|
|
* @param options.minimumGasLimit
|
|
* @param options.transaction
|
|
* @returns {GasFeeErrorsReturnType}
|
|
*/
|
|
export function useGasFeeErrors({
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
isGasEstimatesLoading,
|
|
gasLimit,
|
|
gasPrice,
|
|
maxPriorityFeePerGas,
|
|
maxFeePerGas,
|
|
minimumCostInHexWei,
|
|
minimumGasLimit,
|
|
transaction,
|
|
}) {
|
|
const supportsEIP1559 =
|
|
useSelector(checkNetworkAndAccountSupports1559) &&
|
|
!isLegacyTransaction(transaction?.txParams);
|
|
|
|
const isFeeMarketGasEstimate =
|
|
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET;
|
|
|
|
// Get all errors
|
|
const gasLimitError = validateGasLimit(gasLimit, minimumGasLimit);
|
|
|
|
const maxPriorityFeeError = validateMaxPriorityFee(
|
|
maxPriorityFeePerGas,
|
|
supportsEIP1559,
|
|
);
|
|
|
|
const maxFeeError = validateMaxFee(
|
|
maxFeePerGas,
|
|
maxPriorityFeeError,
|
|
maxPriorityFeePerGas,
|
|
supportsEIP1559,
|
|
);
|
|
|
|
const gasPriceError = validateGasPrice(
|
|
isFeeMarketGasEstimate,
|
|
gasPrice,
|
|
supportsEIP1559,
|
|
transaction,
|
|
);
|
|
|
|
// Get all warnings
|
|
const maxPriorityFeeWarning = getMaxPriorityFeeWarning(
|
|
gasFeeEstimates,
|
|
isFeeMarketGasEstimate,
|
|
isGasEstimatesLoading,
|
|
maxPriorityFeePerGas,
|
|
supportsEIP1559,
|
|
);
|
|
|
|
const maxFeeWarning = getMaxFeeWarning(
|
|
gasFeeEstimates,
|
|
isGasEstimatesLoading,
|
|
isFeeMarketGasEstimate,
|
|
maxFeeError,
|
|
maxPriorityFeeError,
|
|
maxFeePerGas,
|
|
supportsEIP1559,
|
|
);
|
|
|
|
// Separating errors from warnings so we can know which value problems
|
|
// are blocking or simply useful information for the users
|
|
|
|
const gasErrors = useMemo(() => {
|
|
const errors = {};
|
|
if (gasLimitError) {
|
|
errors.gasLimit = gasLimitError;
|
|
}
|
|
if (maxPriorityFeeError) {
|
|
errors.maxPriorityFee = maxPriorityFeeError;
|
|
}
|
|
if (maxFeeError) {
|
|
errors.maxFee = maxFeeError;
|
|
}
|
|
if (gasPriceError) {
|
|
errors.gasPrice = gasPriceError;
|
|
}
|
|
return errors;
|
|
}, [gasLimitError, maxPriorityFeeError, maxFeeError, gasPriceError]);
|
|
|
|
const gasWarnings = useMemo(() => {
|
|
const warnings = {};
|
|
if (maxPriorityFeeWarning) {
|
|
warnings.maxPriorityFee = maxPriorityFeeWarning;
|
|
}
|
|
if (maxFeeWarning) {
|
|
warnings.maxFee = maxFeeWarning;
|
|
}
|
|
return warnings;
|
|
}, [maxPriorityFeeWarning, maxFeeWarning]);
|
|
|
|
const estimatesUnavailableWarning =
|
|
supportsEIP1559 && !isFeeMarketGasEstimate;
|
|
|
|
// Determine if we have any errors which should block submission
|
|
const hasGasErrors = Boolean(Object.keys(gasErrors).length);
|
|
|
|
// Combine the warnings and errors into one object for easier use within the UI.
|
|
// This object should have no effect on whether or not the user can submit the form
|
|
const errorsAndWarnings = useMemo(
|
|
() => ({
|
|
...gasWarnings,
|
|
...gasErrors,
|
|
}),
|
|
[gasErrors, gasWarnings],
|
|
);
|
|
|
|
const { balance: ethBalance } = useSelector(getSelectedAccount, shallowEqual);
|
|
const balanceError = hasBalanceError(
|
|
minimumCostInHexWei,
|
|
transaction,
|
|
ethBalance,
|
|
);
|
|
|
|
return {
|
|
gasErrors: errorsAndWarnings,
|
|
hasGasErrors,
|
|
gasWarnings,
|
|
balanceError,
|
|
estimatesUnavailableWarning,
|
|
hasSimulationError: Boolean(transaction?.simulationFails),
|
|
};
|
|
}
|