1
0
Fork 0
metamask-extension/ui/pages/confirm-transaction-base/confirm-transaction-base.co...

399 lines
12 KiB
JavaScript

import { connect } from 'react-redux';
import { compose } from 'redux';
import { withRouter } from 'react-router-dom';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { showCustodianDeepLink } from '@metamask-institutional/extension';
import { mmiActionsFactory } from '../../store/institutional/institution-background';
///: END:ONLY_INCLUDE_IN
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
import {
updateCustomNonce,
cancelTx,
cancelTxs,
updateAndApproveTx,
showModal,
getNextNonce,
tryReverseResolveAddress,
setDefaultHomeActiveTabName,
addToAddressBook,
} from '../../store/actions';
import { isBalanceSufficient } from '../send/send.utils';
import { shortenAddress, valuesFor } from '../../helpers/utils/util';
import {
getAdvancedInlineGasShown,
getCustomNonceValue,
getIsMainnet,
getKnownMethodData,
getMetaMaskAccounts,
getUseNonceField,
transactionFeeSelector,
getNoGasPriceFetched,
getIsEthGasPriceFetched,
getShouldShowFiat,
checkNetworkAndAccountSupports1559,
getPreferences,
doesAddressRequireLedgerHidConnection,
getTokenList,
getIsMultiLayerFeeNetwork,
getIsBuyableChain,
getEnsResolutionByAddress,
getUnapprovedTransaction,
getFullTxData,
getUseCurrencyRateCheck,
getUnapprovedTransactions,
} from '../../selectors';
import { getMostRecentOverviewPage } from '../../ducks/history/history';
import {
isAddressLedger,
updateGasFees,
getIsGasEstimatesLoading,
getNativeCurrency,
getSendToAccounts,
getProviderConfig,
} from '../../ducks/metamask/metamask';
import {
addHexPrefix,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getEnvironmentType,
///: END:ONLY_INCLUDE_IN
} from '../../../app/scripts/lib/util';
import {
parseStandardTokenTransactionData,
transactionMatchesNetwork,
txParamsAreDappSuggested,
} from '../../../shared/modules/transaction.utils';
import {
isEmptyHexString,
toChecksumHexAddress,
} from '../../../shared/modules/hexstring-utils';
import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { getAccountType } from '../../selectors/selectors';
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../shared/constants/app';
import { getIsNoteToTraderSupported } from '../../selectors/institutional/selectors';
import { showCustodyConfirmLink } from '../../store/institutional/institution-actions';
///: END:ONLY_INCLUDE_IN
import {
TransactionStatus,
TransactionType,
} from '../../../shared/constants/transaction';
import { getTokenAddressParam } from '../../helpers/utils/token-util';
import { calcGasTotal } from '../../../shared/lib/transactions-controller-utils';
import ConfirmTransactionBase from './confirm-transaction-base.component';
let customNonceValue = '';
const customNonceMerge = (txData) =>
customNonceValue
? {
...txData,
customNonceValue,
}
: txData;
function addressIsNew(toAccounts, newAddress) {
const newAddressNormalized = newAddress.toLowerCase();
const foundMatching = toAccounts.some(
({ address }) => address.toLowerCase() === newAddressNormalized,
);
return !foundMatching;
}
const mapStateToProps = (state, ownProps) => {
const {
toAddress: propsToAddress,
customTxParamsData,
match: { params = {} },
} = ownProps;
const { id: paramsTransactionId } = params;
const isMainnet = getIsMainnet(state);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const envType = getEnvironmentType();
const isNotification = envType === ENVIRONMENT_TYPE_NOTIFICATION;
///: END:ONLY_INCLUDE_IN
const isGasEstimatesLoading = getIsGasEstimatesLoading(state);
const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state);
const isBuyableChain = getIsBuyableChain(state);
const { confirmTransaction, metamask } = state;
const { conversionRate, identities, addressBook, networkId, nextNonce } =
metamask;
const unapprovedTxs = getUnapprovedTransactions(state);
const { chainId } = getProviderConfig(state);
const { tokenData, txData, tokenProps, nonce } = confirmTransaction;
const { txParams = {}, id: transactionId, type } = txData;
const txId = transactionId || Number(paramsTransactionId);
const transaction = getUnapprovedTransaction(state, txId);
const {
from: fromAddress,
to: txParamsToAddress,
gasPrice,
gas: gasLimit,
value: amount,
data,
} = (transaction && transaction.txParams) || txParams;
const accounts = getMetaMaskAccounts(state);
const transactionData = parseStandardTokenTransactionData(data);
const tokenToAddress = getTokenAddressParam(transactionData);
const { balance } = accounts[fromAddress];
const { name: fromName } = identities[fromAddress];
const isSendingAmount =
type === TransactionType.simpleSend || !isEmptyHexString(amount);
const toAddress = isSendingAmount
? txParamsToAddress
: propsToAddress || tokenToAddress || txParamsToAddress;
const toAccounts = getSendToAccounts(state);
const tokenList = getTokenList(state);
const toName =
identities[toAddress]?.name ||
tokenList[toAddress?.toLowerCase()]?.name ||
shortenAddress(toChecksumHexAddress(toAddress));
const checksummedAddress = toChecksumHexAddress(toAddress);
const addressBookObject =
addressBook &&
addressBook[chainId] &&
addressBook[chainId][checksummedAddress];
const toEns = getEnsResolutionByAddress(state, checksummedAddress);
const toNickname = addressBookObject ? addressBookObject.name : '';
const transactionStatus = transaction ? transaction.status : '';
const supportsEIP1559 =
checkNetworkAndAccountSupports1559(state) && !isLegacyTransaction(txParams);
const {
hexTransactionAmount,
hexMaximumTransactionFee,
hexTransactionTotal,
gasEstimationObject,
} = transactionFeeSelector(state, transaction);
const currentNetworkUnapprovedTxs = Object.keys(unapprovedTxs)
.filter((key) =>
transactionMatchesNetwork(unapprovedTxs[key], chainId, networkId),
)
.reduce((acc, key) => ({ ...acc, [key]: unapprovedTxs[key] }), {});
const unapprovedTxCount = valuesFor(currentNetworkUnapprovedTxs).length;
const insufficientBalance = !isBalanceSufficient({
amount,
gasTotal: calcGasTotal(gasLimit, gasPrice),
balance,
conversionRate,
});
const methodData = getKnownMethodData(state, data) || {};
const fullTxData = getFullTxData(
state,
txId,
TransactionStatus.unapproved,
customTxParamsData,
);
customNonceValue = getCustomNonceValue(state);
const isEthGasPrice = getIsEthGasPriceFetched(state);
const noGasPrice = !supportsEIP1559 && getNoGasPriceFetched(state);
const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state);
const gasFeeIsCustom =
fullTxData.userFeeLevel === CUSTOM_GAS_ESTIMATE ||
txParamsAreDappSuggested(fullTxData);
const fromAddressIsLedger = isAddressLedger(state, fromAddress);
const nativeCurrency = getNativeCurrency(state);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const accountType = getAccountType(state, fromAddress);
const fromChecksumHexAddress = toChecksumHexAddress(fromAddress);
const isNoteToTraderSupported = getIsNoteToTraderSupported(
state,
fromChecksumHexAddress,
);
///: END:ONLY_INCLUDE_IN
const hardwareWalletRequiresConnection =
doesAddressRequireLedgerHidConnection(state, fromAddress);
const isMultiLayerFeeNetwork = getIsMultiLayerFeeNetwork(state);
return {
balance,
fromAddress,
fromName,
toAccounts,
toAddress,
toEns,
toName,
toNickname,
hexTransactionAmount,
hexMaximumTransactionFee,
hexTransactionTotal,
txData: fullTxData,
tokenData,
methodData,
tokenProps,
conversionRate,
transactionStatus,
nonce,
unapprovedTxs,
unapprovedTxCount,
customGas: {
gasLimit,
gasPrice,
},
advancedInlineGasShown: getAdvancedInlineGasShown(state),
useNonceField: getUseNonceField(state),
customNonceValue,
insufficientBalance,
hideFiatConversion: !getShouldShowFiat(state),
type,
nextNonce,
mostRecentOverviewPage: getMostRecentOverviewPage(state),
isMainnet,
isEthGasPrice,
noGasPrice,
supportsEIP1559,
gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing,
useNativeCurrencyAsPrimaryCurrency,
maxFeePerGas: gasEstimationObject.maxFeePerGas,
maxPriorityFeePerGas: gasEstimationObject.maxPriorityFeePerGas,
baseFeePerGas: gasEstimationObject.baseFeePerGas,
gasFeeIsCustom,
showLedgerSteps: fromAddressIsLedger,
nativeCurrency,
hardwareWalletRequiresConnection,
isMultiLayerFeeNetwork,
chainId,
isBuyableChain,
useCurrencyRateCheck: getUseCurrencyRateCheck(state),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
accountType,
isNoteToTraderSupported,
isNotification,
///: END:ONLY_INCLUDE_IN
};
};
export const mapDispatchToProps = (dispatch) => {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const mmiActions = mmiActionsFactory();
///: END:ONLY_INCLUDE_IN
return {
tryReverseResolveAddress: (address) => {
return dispatch(tryReverseResolveAddress(address));
},
updateCustomNonce: (value) => {
customNonceValue = value;
dispatch(updateCustomNonce(value));
},
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
showTransactionConfirmedModal: ({ onSubmit }) => {
return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onSubmit }));
},
showRejectTransactionsConfirmationModal: ({
onSubmit,
unapprovedTxCount,
}) => {
return dispatch(
showModal({ name: 'REJECT_TRANSACTIONS', onSubmit, unapprovedTxCount }),
);
},
cancelTransaction: ({ id }) => dispatch(cancelTx({ id })),
cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)),
sendTransaction: (txData) =>
dispatch(updateAndApproveTx(customNonceMerge(txData))),
getNextNonce: () => dispatch(getNextNonce()),
setDefaultHomeActiveTabName: (tabName) =>
dispatch(setDefaultHomeActiveTabName(tabName)),
updateTransactionGasFees: (gasFees) => {
dispatch(updateGasFees({ ...gasFees, expectHexWei: true }));
},
addToAddressBookIfNew: (newAddress, toAccounts, nickname = '') => {
const hexPrefixedAddress = addHexPrefix(newAddress);
if (addressIsNew(toAccounts, hexPrefixedAddress)) {
dispatch(addToAddressBook(hexPrefixedAddress, nickname));
}
},
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getCustodianConfirmDeepLink: (id) =>
dispatch(mmiActions.getCustodianConfirmDeepLink(id)),
showTransactionsFailedModal: (errorMessage, closeNotification) =>
dispatch(
showModal({
name: 'TRANSACTION_FAILED',
errorMessage,
closeNotification,
}),
),
showCustodianDeepLink: ({
txId,
fromAddress,
closeNotification,
onDeepLinkFetched,
onDeepLinkShown,
}) =>
showCustodianDeepLink({
dispatch,
mmiActions,
txId,
fromAddress,
closeNotification,
onDeepLinkFetched,
onDeepLinkShown,
showCustodyConfirmLink,
}),
setWaitForConfirmDeepLinkDialog: (wait) =>
dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(wait)),
///: END:ONLY_INCLUDE_IN
};
};
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { txData, unapprovedTxs } = stateProps;
const {
cancelAllTransactions: dispatchCancelAllTransactions,
updateTransactionGasFees: dispatchUpdateTransactionGasFees,
...otherDispatchProps
} = dispatchProps;
let isMainBetaFlask = ownProps.isMainBetaFlask || false;
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
if (ownProps.isMainBetaFlask === undefined) {
isMainBetaFlask = true;
}
///: END:ONLY_INCLUDE_IN
return {
...stateProps,
...otherDispatchProps,
...ownProps,
cancelAllTransactions: () =>
dispatchCancelAllTransactions(valuesFor(unapprovedTxs)),
updateGasAndCalculate: ({ gasLimit, gasPrice }) => {
dispatchUpdateTransactionGasFees({
gasLimit,
gasPrice,
transaction: txData,
});
},
isMainBetaFlask,
};
};
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps, mergeProps),
)(ConfirmTransactionBase);