mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-25 11:28:51 +01:00
337 lines
12 KiB
JavaScript
337 lines
12 KiB
JavaScript
import { useCallback, useEffect, useState } from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
|
|
import {
|
|
CUSTOM_GAS_ESTIMATE,
|
|
GasRecommendations,
|
|
EditGasModes,
|
|
PriorityLevels,
|
|
} from '../../../shared/constants/gas';
|
|
import { GAS_FORM_ERRORS } from '../../helpers/constants/gas';
|
|
import {
|
|
checkNetworkAndAccountSupports1559,
|
|
getAdvancedInlineGasShown,
|
|
} from '../../selectors';
|
|
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
|
|
import { useGasFeeEstimates } from '../useGasFeeEstimates';
|
|
|
|
import { editGasModeIsSpeedUpOrCancel } from '../../helpers/utils/gas';
|
|
import { hexToDecimal } from '../../../shared/modules/conversion.utils';
|
|
import { Numeric } from '../../../shared/modules/Numeric';
|
|
import { EtherDenomination } from '../../../shared/constants/common';
|
|
import { useGasFeeErrors } from './useGasFeeErrors';
|
|
import { useGasPriceInput } from './useGasPriceInput';
|
|
import { useMaxFeePerGasInput } from './useMaxFeePerGasInput';
|
|
import { useMaxPriorityFeePerGasInput } from './useMaxPriorityFeePerGasInput';
|
|
import { useGasEstimates } from './useGasEstimates';
|
|
import { useTransactionFunctions } from './useTransactionFunctions';
|
|
|
|
/**
|
|
* In EIP_1559_V2 implementation as used by useGasfeeInputContext() the use of this hook is evolved.
|
|
* It is no longer used to keep transient state of advance gas fee inputs.
|
|
* Transient state of inputs is maintained locally in /ui/components/app/advance-gas-fee-popover component.
|
|
*
|
|
* This hook is used now as source of shared data about transaction, it shares details of gas fee in transaction,
|
|
* estimate used, is EIP-1559 supported and other details. It also have methods to update transaction.
|
|
*
|
|
* Transaction is used as single source of truth and as transaction is updated the fields shared by hook are
|
|
* also updated using useEffect hook.
|
|
*
|
|
* It will be useful to plan a task to create a new hook of this shared information from this hook.
|
|
* Methods like setEstimateToUse, onManualChange are deprecated in context of EIP_1559_V2 implementation.
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} GasFeeInputReturnType
|
|
* @property {object} [transaction] - .
|
|
* @property {DecGweiString} [maxFeePerGas] - the maxFeePerGas input value.
|
|
* @property {DecGweiString} [maxPriorityFeePerGas] - the maxPriorityFeePerGas
|
|
* input value.
|
|
* @property {DecGweiString} [gasPrice] - the gasPrice input value.
|
|
* @property {(DecGweiString) => void} setGasPrice - state setter method to
|
|
* update the gasPrice.
|
|
* @property {DecGweiString} gasLimit - the gasLimit input value.
|
|
* @property {(DecGweiString) => void} setGasLimit - state setter method to
|
|
* update the gasLimit.
|
|
* @property {DecGweiString} [properGasLimit] - proper gas limit.
|
|
* @property {string} [editGasMode] - one of CANCEL, SPEED-UP, MODIFY_IN_PLACE, SWAPS.
|
|
* @property {EstimateLevel} [estimateToUse] - the estimate level currently
|
|
* selected. This will be null if the user has ejected from using the estimates.
|
|
* @property {boolean} [isGasEstimatesLoading] - true if gas estimate is loading.
|
|
* @property {DecGweiString} [maximumCostInHexWei] - maximum cost of transaction in HexWei.
|
|
* @property {DecGweiString} [minimumCostInHexWei] - minimum cost of transaction in HexWei.
|
|
* @property {string} [estimateUsed] - estimate used in the transaction.
|
|
* @property {boolean} [isNetworkBusy] - true if network is busy.
|
|
* @property {() => void} [onManualChange] - function to call when transaciton is manually changed.
|
|
* @property {boolean} [balanceError] - true if user balance is less than transaction value.
|
|
* @property {object} [gasErrors] - object of gas errors.
|
|
* @property {boolean} [hasGasErrors] - true if there are gas errors.
|
|
* @property {boolean} [hasSimulationError] - true if simulation error exists.
|
|
* @property {number} [minimumGasLimitDec] - minimum gas limit in decimals.
|
|
* @property {boolean} [supportsEIP1559] - true if EIP1559 is cupported.
|
|
* @property {() => void} cancelTransaction - cancel the transaction.
|
|
* @property {() => void} speedUpTransaction - speed up the transaction.
|
|
* @property {(string, number, number, number, string) => void} updateTransaction - update the transaction.
|
|
* @property {(boolean) => void} updateTransactionToTenPercentIncreasedGasFee - update the cancel / speed transaction to
|
|
* gas fee which is equal to current gas fee +10 percent.
|
|
* @property {(string) => void} updateTransactionUsingDAPPSuggestedValues - update the transaction to DAPP suggested gas value.
|
|
* @property {(string) => void} updateTransactionUsingEstimate - update the transaction using the estimate passed.
|
|
*/
|
|
|
|
/**
|
|
* Uses gasFeeEstimates and state to keep track of user gas fee inputs.
|
|
* Will update the gas fee state when estimates update if the user has not yet
|
|
* modified the fields.
|
|
*
|
|
* @param {GasRecommendations} [defaultEstimateToUse] - which estimate
|
|
* level to default the 'estimateToUse' state variable to.
|
|
* @param {object} [_transaction]
|
|
* @param {string} [minimumGasLimit]
|
|
* @param {EditGasModes[keyof EditGasModes]} editGasMode
|
|
* @returns {GasFeeInputReturnType & import(
|
|
* './useGasFeeEstimates'
|
|
* ).GasEstimates} gas fee input state and the GasFeeEstimates object
|
|
*/
|
|
|
|
const GAS_LIMIT_TOO_HIGH_IN_ETH = '1';
|
|
export function useGasFeeInputs(
|
|
defaultEstimateToUse = GasRecommendations.medium,
|
|
_transaction,
|
|
minimumGasLimit = '0x5208',
|
|
editGasMode = EditGasModes.modifyInPlace,
|
|
) {
|
|
const initialRetryTxMeta = {
|
|
txParams: _transaction?.txParams,
|
|
id: _transaction?.id,
|
|
userFeeLevel: _transaction?.userFeeLevel,
|
|
originalGasEstimate: _transaction?.originalGasEstimate,
|
|
userEditedGasLimit: _transaction?.userEditedGasLimit,
|
|
};
|
|
|
|
if (_transaction?.previousGas) {
|
|
initialRetryTxMeta.previousGas = _transaction?.previousGas;
|
|
}
|
|
|
|
const [retryTxMeta, setRetryTxMeta] = useState(initialRetryTxMeta);
|
|
|
|
const transaction = editGasModeIsSpeedUpOrCancel(editGasMode)
|
|
? retryTxMeta
|
|
: _transaction;
|
|
|
|
const supportsEIP1559 =
|
|
useSelector(checkNetworkAndAccountSupports1559) &&
|
|
!isLegacyTransaction(transaction?.txParams);
|
|
|
|
// We need the gas estimates from the GasFeeController in the background.
|
|
// Calling this hooks initiates polling for new gas estimates and returns the
|
|
// current estimate.
|
|
const {
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
isGasEstimatesLoading,
|
|
isNetworkBusy,
|
|
} = useGasFeeEstimates();
|
|
|
|
const userPrefersAdvancedGas = useSelector(getAdvancedInlineGasShown);
|
|
|
|
const [estimateToUse, setInternalEstimateToUse] = useState(() => {
|
|
if (
|
|
userPrefersAdvancedGas &&
|
|
transaction?.txParams?.maxPriorityFeePerGas &&
|
|
transaction?.txParams?.maxFeePerGas
|
|
) {
|
|
return null;
|
|
}
|
|
if (transaction) {
|
|
return transaction?.userFeeLevel || null;
|
|
}
|
|
return defaultEstimateToUse;
|
|
});
|
|
|
|
const [estimateUsed, setEstimateUsed] = useState(() => {
|
|
if (estimateToUse) {
|
|
return estimateToUse;
|
|
}
|
|
return PriorityLevels.custom;
|
|
});
|
|
|
|
const [gasLimit, setGasLimit] = useState(() =>
|
|
Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')),
|
|
);
|
|
|
|
const properGasLimit = Number(hexToDecimal(transaction?.originalGasEstimate));
|
|
|
|
/**
|
|
* In EIP-1559 V2 designs change to gas estimate is always updated to transaction
|
|
* Thus callback setEstimateToUse can be deprecate in favor of this useEffect
|
|
* so that transaction is source of truth whenever possible.
|
|
*/
|
|
useEffect(() => {
|
|
if (supportsEIP1559) {
|
|
if (transaction?.userFeeLevel) {
|
|
setInternalEstimateToUse(transaction?.userFeeLevel);
|
|
}
|
|
|
|
const maximumGas = new Numeric(transaction?.txParams?.gas ?? '0x0', 16)
|
|
.times(new Numeric(transaction?.txParams?.maxFeePerGas ?? '0x0', 16))
|
|
.toPrefixedHexString();
|
|
|
|
const fee = new Numeric(maximumGas, 16, EtherDenomination.WEI)
|
|
.toDenomination(EtherDenomination.ETH)
|
|
.toBase(10)
|
|
.toString();
|
|
|
|
if (Number(fee) > Number(GAS_LIMIT_TOO_HIGH_IN_ETH)) {
|
|
setEstimateUsed(PriorityLevels.dappSuggestedHigh);
|
|
} else if (transaction?.userFeeLevel) {
|
|
setEstimateUsed(transaction?.userFeeLevel);
|
|
}
|
|
|
|
setGasLimit(Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')));
|
|
}
|
|
}, [
|
|
setEstimateUsed,
|
|
setGasLimit,
|
|
setInternalEstimateToUse,
|
|
supportsEIP1559,
|
|
transaction,
|
|
]);
|
|
|
|
const { gasPrice, setGasPrice, setGasPriceHasBeenManuallySet } =
|
|
useGasPriceInput({
|
|
estimateToUse,
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
transaction,
|
|
});
|
|
|
|
const { maxFeePerGas, setMaxFeePerGas } = useMaxFeePerGasInput({
|
|
estimateToUse,
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
transaction,
|
|
});
|
|
|
|
const { maxPriorityFeePerGas, setMaxPriorityFeePerGas } =
|
|
useMaxPriorityFeePerGasInput({
|
|
estimateToUse,
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
transaction,
|
|
});
|
|
|
|
const { estimatedMinimumNative, maximumCostInHexWei, minimumCostInHexWei } =
|
|
useGasEstimates({
|
|
editGasMode,
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
gasLimit,
|
|
gasPrice,
|
|
maxFeePerGas,
|
|
maxPriorityFeePerGas,
|
|
minimumGasLimit,
|
|
transaction,
|
|
});
|
|
|
|
const { balanceError, gasErrors, hasGasErrors, hasSimulationError } =
|
|
useGasFeeErrors({
|
|
gasEstimateType,
|
|
gasFeeEstimates,
|
|
isGasEstimatesLoading,
|
|
gasLimit,
|
|
gasPrice,
|
|
maxPriorityFeePerGas,
|
|
maxFeePerGas,
|
|
minimumCostInHexWei,
|
|
minimumGasLimit,
|
|
transaction,
|
|
});
|
|
|
|
const handleGasLimitOutOfBoundError = useCallback(() => {
|
|
if (gasErrors.gasLimit === GAS_FORM_ERRORS.GAS_LIMIT_OUT_OF_BOUNDS) {
|
|
const transactionGasLimitDec = hexToDecimal(transaction?.txParams?.gas);
|
|
const minimumGasLimitDec = hexToDecimal(minimumGasLimit);
|
|
setGasLimit(
|
|
transactionGasLimitDec > minimumGasLimitDec
|
|
? transactionGasLimitDec
|
|
: minimumGasLimitDec,
|
|
);
|
|
}
|
|
}, [minimumGasLimit, gasErrors.gasLimit, transaction]);
|
|
|
|
const {
|
|
cancelTransaction,
|
|
speedUpTransaction,
|
|
updateTransaction,
|
|
updateTransactionToTenPercentIncreasedGasFee,
|
|
updateTransactionUsingDAPPSuggestedValues,
|
|
updateTransactionUsingEstimate,
|
|
} = useTransactionFunctions({
|
|
defaultEstimateToUse,
|
|
editGasMode,
|
|
gasFeeEstimates,
|
|
gasLimit,
|
|
maxPriorityFeePerGas,
|
|
minimumGasLimit,
|
|
transaction,
|
|
setRetryTxMeta,
|
|
});
|
|
|
|
const onManualChange = useCallback(() => {
|
|
setInternalEstimateToUse(CUSTOM_GAS_ESTIMATE);
|
|
handleGasLimitOutOfBoundError();
|
|
// Restore existing values
|
|
setGasPrice(gasPrice);
|
|
setGasLimit(gasLimit);
|
|
setMaxFeePerGas(maxFeePerGas);
|
|
setMaxPriorityFeePerGas(maxPriorityFeePerGas);
|
|
setGasPriceHasBeenManuallySet(true);
|
|
setEstimateUsed('custom');
|
|
}, [
|
|
setInternalEstimateToUse,
|
|
handleGasLimitOutOfBoundError,
|
|
setGasPrice,
|
|
gasPrice,
|
|
setGasLimit,
|
|
gasLimit,
|
|
setMaxFeePerGas,
|
|
maxFeePerGas,
|
|
setMaxPriorityFeePerGas,
|
|
maxPriorityFeePerGas,
|
|
setGasPriceHasBeenManuallySet,
|
|
]);
|
|
|
|
return {
|
|
transaction,
|
|
maxFeePerGas,
|
|
maxPriorityFeePerGas,
|
|
gasPrice,
|
|
setGasPrice,
|
|
gasLimit,
|
|
setGasLimit,
|
|
properGasLimit,
|
|
editGasMode,
|
|
estimateToUse,
|
|
estimatedMinimumNative,
|
|
maximumCostInHexWei,
|
|
minimumCostInHexWei,
|
|
estimateUsed,
|
|
gasFeeEstimates,
|
|
isNetworkBusy,
|
|
onManualChange,
|
|
// error and warnings
|
|
balanceError,
|
|
gasErrors,
|
|
hasGasErrors,
|
|
hasSimulationError,
|
|
minimumGasLimitDec: hexToDecimal(minimumGasLimit),
|
|
supportsEIP1559,
|
|
cancelTransaction,
|
|
speedUpTransaction,
|
|
updateTransaction,
|
|
updateTransactionToTenPercentIncreasedGasFee,
|
|
updateTransactionUsingDAPPSuggestedValues,
|
|
updateTransactionUsingEstimate,
|
|
};
|
|
}
|