import React, { useEffect, useRef, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import ConfirmTransactionBase from '../confirm-transaction-base';
import { EditGasModes } from '../../../shared/constants/gas';
import {
showModal,
updateCustomNonce,
getNextNonce,
} from '../../store/actions';
import { getTokenApprovedParam } from '../../helpers/utils/token-util';
import { readAddressAsContract } from '../../../shared/modules/contract-utils';
import { GasFeeContextProvider } from '../../contexts/gasFee';
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
import {
getNativeCurrency,
isAddressLedger,
} from '../../ducks/metamask/metamask';
import ConfirmContractInteraction from '../confirm-contract-interaction';
import {
getCurrentCurrency,
getSubjectMetadata,
getUseNonceField,
getCustomNonceValue,
getNextSuggestedNonce,
getCurrentChainId,
getRpcPrefsForCurrentProvider,
getIsMultiLayerFeeNetwork,
checkNetworkAndAccountSupports1559,
getUseCurrencyRateCheck,
getPreferences,
} from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { useSimulationFailureWarning } from '../../hooks/useSimulationFailureWarning';
import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover';
import EditGasFeePopover from '../../components/app/edit-gas-fee-popover';
import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
import Loading from '../../components/ui/loading-screen';
import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
import { TokenStandard } from '../../../shared/constants/transaction';
import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils';
import TokenAllowance from '../token-allowance/token-allowance';
import { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content';
const isAddressLedgerByFromAddress = (address) => (state) => {
return isAddressLedger(state, address);
};
export default function ConfirmApprove({
assetStandard,
assetName,
userBalance,
tokenSymbol,
decimals,
tokenImage,
tokenAmount,
tokenId,
userAddress,
toAddress,
tokenAddress,
transaction,
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
hexMinimumTransactionFee,
isSetApproveForAll,
}) {
const dispatch = useDispatch();
const { txParams: { data: transactionData } = {} } = transaction;
const currentCurrency = useSelector(getCurrentCurrency);
const nativeCurrency = useSelector(getNativeCurrency);
const subjectMetadata = useSelector(getSubjectMetadata);
const useNonceField = useSelector(getUseNonceField);
const nextNonce = useSelector(getNextSuggestedNonce);
const customNonceValue = useSelector(getCustomNonceValue);
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const isMultiLayerFeeNetwork = useSelector(getIsMultiLayerFeeNetwork);
const networkAndAccountSupports1559 = useSelector(
checkNetworkAndAccountSupports1559,
);
const fromAddressIsLedger = useSelector(
isAddressLedgerByFromAddress(userAddress),
);
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const [submitWarning, setSubmitWarning] = useState('');
const [isContract, setIsContract] = useState(false);
const [userAcknowledgedGasMissing, setUserAcknowledgedGasMissing] =
useState(false);
const supportsEIP1559 = networkAndAccountSupports1559;
const previousTokenAmount = useRef(tokenAmount);
const {
approveTransaction,
showCustomizeGasPopover,
closeCustomizeGasPopover,
} = useApproveTransaction();
const renderSimulationFailureWarning = useSimulationFailureWarning(
userAcknowledgedGasMissing,
);
useEffect(() => {
if (customPermissionAmount && previousTokenAmount.current !== tokenAmount) {
setCustomPermissionAmount(tokenAmount);
}
previousTokenAmount.current = tokenAmount;
}, [customPermissionAmount, tokenAmount]);
const prevNonce = useRef(nextNonce);
const prevCustomNonce = useRef(customNonceValue);
useEffect(() => {
if (
prevNonce.current !== nextNonce ||
prevCustomNonce.current !== customNonceValue
) {
if (nextNonce !== null && customNonceValue > nextNonce) {
setSubmitWarning(
`Nonce is higher than suggested nonce of ${nextNonce}`,
);
} else {
setSubmitWarning('');
}
}
prevCustomNonce.current = customNonceValue;
prevNonce.current = nextNonce;
}, [customNonceValue, nextNonce]);
const checkIfContract = useCallback(async () => {
const { isContractAddress } = await readAddressAsContract(
global.eth,
toAddress,
);
setIsContract(isContractAddress);
}, [setIsContract, toAddress]);
useEffect(() => {
checkIfContract();
}, [checkIfContract]);
const { origin } = transaction;
const formattedOrigin = origin || '';
const { iconUrl: siteImage = '' } = subjectMetadata[origin] || {};
// Code below may need a additional look as ERC1155 tokens do not have a name
let tokensText;
if (
assetStandard === TokenStandard.ERC721 ||
assetStandard === TokenStandard.ERC1155
) {
tokensText = assetName;
}
const tokenBalance = userBalance
? calcTokenAmount(userBalance, decimals).toString(10)
: '';
const customData = customPermissionAmount
? getCustomTxParamsData(transactionData, {
customPermissionAmount,
decimals,
})
: null;
const parsedTransactionData =
parseStandardTokenTransactionData(transactionData);
const isApprovalOrRejection = getTokenApprovedParam(parsedTransactionData);
if (tokenSymbol === undefined && assetName === undefined) {
return ;
}
if (assetStandard === undefined) {
return ;
}
if (assetStandard === TokenStandard.ERC20) {
return (
{showCustomizeGasPopover && !supportsEIP1559 && (
)}
{supportsEIP1559 && (
<>
>
)}
);
}
return (
{
dispatch(updateCustomNonce(value));
}}
getNextNonce={() => dispatch(getNextNonce())}
showCustomizeNonceModal={({
/* eslint-disable no-shadow */
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
/* eslint-disable no-shadow */
}) =>
dispatch(
showModal({
name: 'CUSTOMIZE_NONCE',
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
}),
)
}
warning={submitWarning}
txData={transaction}
fromAddressIsLedger={fromAddressIsLedger}
chainId={chainId}
rpcPrefs={rpcPrefs}
isContract={isContract}
isMultiLayerFeeNetwork={isMultiLayerFeeNetwork}
supportsEIP1559={supportsEIP1559}
useCurrencyRateCheck={useCurrencyRateCheck}
useNativeCurrencyAsPrimaryCurrency={
useNativeCurrencyAsPrimaryCurrency
}
/>
{showCustomizeGasPopover && !supportsEIP1559 && (
)}
{supportsEIP1559 && (
<>
>
)}
}
hideSenderToRecipient
customTxParamsData={customData}
assetStandard={assetStandard}
/>
);
}
ConfirmApprove.propTypes = {
assetStandard: PropTypes.string,
assetName: PropTypes.string,
tokenAddress: PropTypes.string,
userBalance: PropTypes.string,
tokenSymbol: PropTypes.string,
decimals: PropTypes.string,
tokenImage: PropTypes.string,
tokenAmount: PropTypes.string,
tokenId: PropTypes.string,
userAddress: PropTypes.string,
toAddress: PropTypes.string,
transaction: PropTypes.shape({
origin: PropTypes.string,
txParams: PropTypes.shape({
data: PropTypes.string,
to: PropTypes.string,
from: PropTypes.string,
}),
}),
ethTransactionTotal: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
hexTransactionTotal: PropTypes.string,
hexMinimumTransactionFee: PropTypes.string,
isSetApproveForAll: PropTypes.bool,
};