import React, { useCallback, useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { isEIP1559Network } from '../../../ducks/metamask/metamask'; import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs'; import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations'; import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas'; import { decGWEIToHexWEI, decimalToHex, hexToDecimal, } from '../../../helpers/utils/conversions.util'; import Popover from '../../ui/popover'; import Button from '../../ui/button'; import EditGasDisplay from '../edit-gas-display'; import EditGasDisplayEducation from '../edit-gas-display-education'; import { I18nContext } from '../../../contexts/i18n'; import { createCancelTransaction, createSpeedUpTransaction, hideModal, hideSidebar, updateTransaction, updateCustomSwapsEIP1559GasParams, } from '../../../store/actions'; import LoadingHeartBeat from '../../ui/loading-heartbeat'; export default function EditGasPopover({ popoverTitle = '', confirmButtonText = '', editGasDisplayProps = {}, defaultEstimateToUse = 'medium', transaction, mode, onClose, minimumGasLimit = GAS_LIMITS.SIMPLE, }) { const t = useContext(I18nContext); const dispatch = useDispatch(); const showSidebar = useSelector((state) => state.appState.sidebar.isOpen); const networkSupports1559 = useSelector(isEIP1559Network); const shouldAnimate = useShouldAnimateGasEstimations(); const showEducationButton = mode === EDIT_GAS_MODES.MODIFY_IN_PLACE && networkSupports1559; const [showEducationContent, setShowEducationContent] = useState(false); const [warning] = useState(null); const [ dappSuggestedGasFeeAcknowledged, setDappSuggestedGasFeeAcknowledged, ] = useState(false); const minimumGasLimitDec = hexToDecimal(minimumGasLimit); const { maxPriorityFeePerGas, setMaxPriorityFeePerGas, maxPriorityFeePerGasFiat, maxFeePerGas, setMaxFeePerGas, maxFeePerGasFiat, estimatedMaximumNative, isGasEstimatesLoading, gasFeeEstimates, gasEstimateType, gasPrice, setGasPrice, gasLimit, setGasLimit, estimateToUse, setEstimateToUse, estimatedMinimumFiat, estimatedMaximumFiat, hasGasErrors, gasErrors, onManualChange, balanceError, estimatesUnavailableWarning, } = useGasFeeInputs(defaultEstimateToUse, transaction, minimumGasLimit, mode); const [showAdvancedForm, setShowAdvancedForm] = useState( !estimateToUse || hasGasErrors, ); /** * Temporary placeholder, this should be managed by the parent component but * we will be extracting this component from the hard to maintain modal/ * sidebar component. For now this is just to be able to appropriately close * the modal in testing */ const closePopover = useCallback(() => { if (onClose) { onClose(); } else if (showSidebar) { dispatch(hideSidebar()); } else { dispatch(hideModal()); } }, [showSidebar, onClose, dispatch]); const onSubmit = useCallback(() => { if (!transaction || !mode) { closePopover(); } const newGasSettings = networkSupports1559 ? { gas: decimalToHex(gasLimit), gasLimit: decimalToHex(gasLimit), maxFeePerGas: decGWEIToHexWEI(maxFeePerGas ?? gasPrice), maxPriorityFeePerGas: decGWEIToHexWEI( maxPriorityFeePerGas ?? maxFeePerGas ?? gasPrice, ), } : { gas: decimalToHex(gasLimit), gasLimit: decimalToHex(gasLimit), gasPrice: decGWEIToHexWEI(gasPrice), }; switch (mode) { case EDIT_GAS_MODES.CANCEL: dispatch(createCancelTransaction(transaction.id, newGasSettings)); break; case EDIT_GAS_MODES.SPEED_UP: dispatch(createSpeedUpTransaction(transaction.id, newGasSettings)); break; case EDIT_GAS_MODES.MODIFY_IN_PLACE: dispatch( updateTransaction({ ...transaction, txParams: { ...transaction.txParams, ...newGasSettings, }, }), ); break; case EDIT_GAS_MODES.SWAPS: // This popover component should only be used for the "FEE_MARKET" type in Swaps. if (networkSupports1559) { dispatch(updateCustomSwapsEIP1559GasParams(newGasSettings)); } break; default: break; } closePopover(); }, [ transaction, mode, dispatch, closePopover, gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas, networkSupports1559, ]); let title = t('editGasTitle'); if (popoverTitle) { title = popoverTitle; } else if (showEducationContent) { title = t('editGasEducationModalTitle'); } else if (mode === EDIT_GAS_MODES.SPEED_UP) { title = t('speedUpPopoverTitle'); } else if (mode === EDIT_GAS_MODES.CANCEL) { title = t('cancelPopoverTitle'); } const footerButtonText = confirmButtonText || t('save'); return ( setShowEducationContent(false) : undefined } footer={ showEducationContent ? null : ( <> ) } >
{showEducationContent ? ( ) : ( <> {process.env.IN_TEST === 'true' ? null : ( )} setShowEducationContent(true)} mode={mode} transaction={transaction} gasErrors={gasErrors} onManualChange={onManualChange} minimumGasLimit={minimumGasLimitDec} balanceError={balanceError} estimatesUnavailableWarning={estimatesUnavailableWarning} {...editGasDisplayProps} /> )}
); } EditGasPopover.propTypes = { popoverTitle: PropTypes.string, editGasDisplayProps: PropTypes.object, confirmButtonText: PropTypes.string, onClose: PropTypes.func, transaction: PropTypes.object, mode: PropTypes.oneOf(Object.values(EDIT_GAS_MODES)), defaultEstimateToUse: PropTypes.string, minimumGasLimit: PropTypes.string, };