|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { addHexPrefix } from '../../../../../app/scripts/lib/util';
|
|
|
|
import {
|
|
|
|
hideModal,
|
|
|
|
createRetryTransaction,
|
|
|
|
createSpeedUpTransaction,
|
|
|
|
} from '../../../../store/actions';
|
|
|
|
import {
|
|
|
|
setCustomGasPrice,
|
|
|
|
setCustomGasLimit,
|
|
|
|
resetCustomData,
|
|
|
|
} from '../../../../ducks/gas/gas.duck';
|
|
|
|
import {
|
|
|
|
getSendMaxModeState,
|
|
|
|
getGasLimit,
|
|
|
|
getGasPrice,
|
|
|
|
getSendAmount,
|
|
|
|
updateGasLimit,
|
|
|
|
updateGasPrice,
|
|
|
|
useCustomGas,
|
|
|
|
getSendAsset,
|
|
|
|
} from '../../../../ducks/send';
|
|
|
|
import {
|
|
|
|
conversionRateSelector as getConversionRate,
|
|
|
|
getCurrentCurrency,
|
|
|
|
getCurrentEthBalance,
|
|
|
|
getIsMainnet,
|
|
|
|
getIsTestnet,
|
|
|
|
getBasicGasEstimateLoadingStatus,
|
|
|
|
getCustomGasLimit,
|
|
|
|
getCustomGasPrice,
|
|
|
|
getDefaultActiveButtonIndex,
|
|
|
|
getRenderableBasicEstimateData,
|
|
|
|
isCustomPriceSafe,
|
|
|
|
isCustomPriceSafeForCustomNetwork,
|
|
|
|
getAveragePriceEstimateInHexWEI,
|
|
|
|
isCustomPriceExcessive,
|
|
|
|
getIsGasEstimatesFetched,
|
|
|
|
getShouldShowFiat,
|
|
|
|
getIsCustomNetworkGasPriceFetched,
|
|
|
|
} from '../../../../selectors';
|
|
|
|
|
|
|
|
import {
|
|
|
|
addHexes,
|
|
|
|
subtractHexWEIsToDec,
|
|
|
|
hexWEIToDecGWEI,
|
|
|
|
getValueFromWeiHex,
|
|
|
|
sumHexWEIsToRenderableFiat,
|
|
|
|
} from '../../../../helpers/utils/conversions.util';
|
|
|
|
import { formatETHFee } from '../../../../helpers/utils/formatters';
|
|
|
|
import {
|
|
|
|
calcGasTotal,
|
|
|
|
isBalanceSufficient,
|
|
|
|
} from '../../../../pages/send/send.utils';
|
|
|
|
import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants';
|
|
|
|
import {
|
|
|
|
ASSET_TYPES,
|
|
|
|
TRANSACTION_STATUSES,
|
|
|
|
} from '../../../../../shared/constants/transaction';
|
|
|
|
import { GAS_LIMITS } from '../../../../../shared/constants/gas';
|
|
|
|
import { updateGasFees } from '../../../../ducks/metamask/metamask';
|
|
|
|
import GasModalPageContainer from './gas-modal-page-container.component';
|
|
|
|
|
|
|
|
const mapStateToProps = (state, ownProps) => {
|
|
|
|
const gasLimit = getGasLimit(state);
|
|
|
|
const gasPrice = getGasPrice(state);
|
|
|
|
const amount = getSendAmount(state);
|
|
|
|
const { currentNetworkTxList } = state.metamask;
|
|
|
|
const { modalState: { props: modalProps } = {} } = state.appState.modal || {};
|
|
|
|
const { txData = {} } = modalProps || {};
|
|
|
|
const { transaction = {}, onSubmit } = ownProps;
|
|
|
|
const selectedTransaction = currentNetworkTxList.find(
|
|
|
|
({ id }) => id === (transaction.id || txData.id),
|
|
|
|
);
|
|
|
|
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state);
|
|
|
|
const asset = getSendAsset(state);
|
|
|
|
|
|
|
|
// a "default" txParams is used during the send flow, since the transaction doesn't exist yet in that case
|
|
|
|
const txParams = selectedTransaction?.txParams
|
|
|
|
? selectedTransaction.txParams
|
|
|
|
: {
|
|
|
|
gas: gasLimit || GAS_LIMITS.SIMPLE,
|
|
|
|
gasPrice: gasPrice || getAveragePriceEstimateInHexWEI(state, true),
|
|
|
|
value: asset.type === ASSET_TYPES.TOKEN ? '0x0' : amount,
|
|
|
|
};
|
|
|
|
|
|
|
|
const { gasPrice: currentGasPrice, gas: currentGasLimit } = txParams;
|
|
|
|
const value = ownProps.transaction?.txParams?.value || txParams.value;
|
|
|
|
const customModalGasPriceInHex = getCustomGasPrice(state) || currentGasPrice;
|
|
|
|
const customModalGasLimitInHex =
|
|
|
|
getCustomGasLimit(state) || currentGasLimit || GAS_LIMITS.SIMPLE;
|
|
|
|
const customGasTotal = calcGasTotal(
|
|
|
|
customModalGasLimitInHex,
|
|
|
|
customModalGasPriceInHex,
|
|
|
|
);
|
|
|
|
|
|
|
|
const gasButtonInfo = getRenderableBasicEstimateData(
|
|
|
|
state,
|
|
|
|
customModalGasLimitInHex,
|
|
|
|
);
|
|
|
|
|
|
|
|
const currentCurrency = getCurrentCurrency(state);
|
|
|
|
const conversionRate = getConversionRate(state);
|
|
|
|
const newTotalFiat = sumHexWEIsToRenderableFiat(
|
|
|
|
[value, customGasTotal],
|
|
|
|
currentCurrency,
|
|
|
|
conversionRate,
|
|
|
|
);
|
|
|
|
|
|
|
|
const { hideBasic } = state.appState.modal.modalState.props;
|
|
|
|
|
|
|
|
const customGasPrice = calcCustomGasPrice(customModalGasPriceInHex);
|
|
|
|
|
|
|
|
const maxModeOn = getSendMaxModeState(state);
|
|
|
|
|
|
|
|
const balance = getCurrentEthBalance(state);
|
|
|
|
|
|
|
|
const isMainnet = getIsMainnet(state);
|
|
|
|
const isTestnet = getIsTestnet(state);
|
|
|
|
const showFiat = getShouldShowFiat(state);
|
|
|
|
|
|
|
|
const newTotalEth =
|
|
|
|
maxModeOn && asset.type === ASSET_TYPES.NATIVE
|
|
|
|
? sumHexWEIsToRenderableEth([balance, '0x0'])
|
|
|
|
: sumHexWEIsToRenderableEth([value, customGasTotal]);
|
|
|
|
|
|
|
|
const sendAmount =
|
|
|
|
maxModeOn && asset.type === ASSET_TYPES.NATIVE
|
|
|
|
? subtractHexWEIsFromRenderableEth(balance, customGasTotal)
|
|
|
|
: sumHexWEIsToRenderableEth([value, '0x0']);
|
|
|
|
|
|
|
|
const insufficientBalance = maxModeOn
|
|
|
|
? false
|
|
|
|
: !isBalanceSufficient({
|
|
|
|
amount: value,
|
|
|
|
gasTotal: customGasTotal,
|
|
|
|
balance,
|
|
|
|
conversionRate,
|
|
|
|
});
|
|
|
|
const isGasEstimate = getIsGasEstimatesFetched(state);
|
|
|
|
const customNetworkEstimateWasFetched = getIsCustomNetworkGasPriceFetched(
|
|
|
|
state,
|
|
|
|
);
|
|
|
|
|
|
|
|
let customPriceIsSafe = true;
|
|
|
|
if ((isMainnet || process.env.IN_TEST) && isGasEstimate) {
|
|
|
|
customPriceIsSafe = isCustomPriceSafe(state);
|
|
|
|
} else if (
|
|
|
|
!(isMainnet || process.env.IN_TEST || isTestnet) &&
|
|
|
|
customNetworkEstimateWasFetched
|
|
|
|
) {
|
|
|
|
customPriceIsSafe = isCustomPriceSafeForCustomNetwork(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
hideBasic,
|
|
|
|
isConfirm: isConfirm(state),
|
|
|
|
customModalGasPriceInHex,
|
|
|
|
customModalGasLimitInHex,
|
|
|
|
customGasPrice,
|
|
|
|
customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
|
|
|
|
customGasTotal,
|
|
|
|
newTotalFiat,
|
|
|
|
customPriceIsSafe,
|
|
|
|
customPriceIsExcessive: isCustomPriceExcessive(state),
|
|
|
|
maxModeOn,
|
|
|
|
gasPriceButtonGroupProps: {
|
|
|
|
buttonDataLoading,
|
|
|
|
defaultActiveButtonIndex: getDefaultActiveButtonIndex(
|
|
|
|
gasButtonInfo,
|
|
|
|
customModalGasPriceInHex,
|
|
|
|
),
|
|
|
|
gasButtonInfo,
|
|
|
|
},
|
|
|
|
infoRowProps: {
|
|
|
|
originalTotalFiat: sumHexWEIsToRenderableFiat(
|
|
|
|
[value, customGasTotal],
|
|
|
|
currentCurrency,
|
|
|
|
conversionRate,
|
|
|
|
),
|
|
|
|
originalTotalEth: sumHexWEIsToRenderableEth([value, customGasTotal]),
|
|
|
|
newTotalFiat: showFiat ? newTotalFiat : '',
|
|
|
|
newTotalEth,
|
|
|
|
transactionFee: sumHexWEIsToRenderableEth(['0x0', customGasTotal]),
|
|
|
|
sendAmount,
|
|
|
|
},
|
|
|
|
transaction: txData || transaction,
|
|
|
|
isSpeedUp: transaction.status === TRANSACTION_STATUSES.SUBMITTED,
|
|
|
|
isRetry: transaction.status === TRANSACTION_STATUSES.FAILED,
|
|
|
|
txId: transaction.id,
|
|
|
|
insufficientBalance,
|
|
|
|
isMainnet,
|
|
|
|
balance,
|
|
|
|
conversionRate,
|
|
|
|
value,
|
|
|
|
onSubmit,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const mapDispatchToProps = (dispatch) => {
|
|
|
|
const updateCustomGasPrice = (newPrice) =>
|
|
|
|
dispatch(setCustomGasPrice(addHexPrefix(newPrice)));
|
|
|
|
|
|
|
|
return {
|
|
|
|
cancelAndClose: () => {
|
|
|
|
dispatch(resetCustomData());
|
|
|
|
dispatch(hideModal());
|
|
|
|
},
|
|
|
|
hideModal: () => dispatch(hideModal()),
|
|
|
|
useCustomGas: () => dispatch(useCustomGas()),
|
|
|
|
updateTransactionGasFees: (gasFees) => {
|
|
|
|
dispatch(updateGasFees({ ...gasFees, expectHexWei: true }));
|
|
|
|
},
|
|
|
|
updateCustomGasPrice,
|
|
|
|
updateCustomGasLimit: (newLimit) =>
|
|
|
|
dispatch(setCustomGasLimit(addHexPrefix(newLimit))),
|
|
|
|
setGasData: (newLimit, newPrice) => {
|
|
|
|
dispatch(updateGasLimit(newLimit));
|
|
|
|
dispatch(updateGasPrice(newPrice));
|
|
|
|
},
|
|
|
|
createRetryTransaction: (txId, customGasSettings) => {
|
|
|
|
return dispatch(createRetryTransaction(txId, customGasSettings));
|
|
|
|
},
|
|
|
|
createSpeedUpTransaction: (txId, customGasSettings) => {
|
|
|
|
return dispatch(createSpeedUpTransaction(txId, customGasSettings));
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|
|
|
const {
|
|
|
|
gasPriceButtonGroupProps,
|
|
|
|
// eslint-disable-next-line no-shadow
|
|
|
|
isConfirm,
|
|
|
|
txId,
|
|
|
|
isSpeedUp,
|
|
|
|
isRetry,
|
|
|
|
insufficientBalance,
|
|
|
|
customGasPrice,
|
|
|
|
customGasLimit,
|
|
|
|
transaction,
|
|
|
|
} = stateProps;
|
|
|
|
const {
|
|
|
|
useCustomGas: dispatchUseCustomGas,
|
|
|
|
setGasData: dispatchSetGasData,
|
|
|
|
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
|
|
|
|
createRetryTransaction: dispatchCreateRetryTransaction,
|
|
|
|
updateTransactionGasFees: dispatchUpdateTransactionGasFees,
|
|
|
|
cancelAndClose: dispatchCancelAndClose,
|
|
|
|
hideModal: dispatchHideModal,
|
|
|
|
...otherDispatchProps
|
|
|
|
} = dispatchProps;
|
|
|
|
|
|
|
|
return {
|
|
|
|
...stateProps,
|
|
|
|
...otherDispatchProps,
|
|
|
|
...ownProps,
|
|
|
|
onSubmit: (gasLimit, gasPrice) => {
|
|
|
|
if (ownProps.onSubmit) {
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
ownProps.onSubmit({ gasLimit, gasPrice });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (isConfirm) {
|
|
|
|
dispatchUpdateTransactionGasFees({
|
|
|
|
gasLimit,
|
|
|
|
gasPrice,
|
|
|
|
transaction,
|
|
|
|
isModal: true,
|
|
|
|
});
|
|
|
|
dispatchHideModal();
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
} else if (isSpeedUp) {
|
|
|
|
dispatchCreateSpeedUpTransaction(txId, { gasPrice, gasLimit });
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
} else if (isRetry) {
|
|
|
|
dispatchCreateRetryTransaction(txId, { gasPrice, gasLimit });
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
} else {
|
|
|
|
dispatchSetGasData(gasLimit, gasPrice);
|
|
|
|
dispatchUseCustomGas();
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
gasPriceButtonGroupProps: {
|
|
|
|
...gasPriceButtonGroupProps,
|
|
|
|
handleGasPriceSelection: ({ gasPrice }) =>
|
|
|
|
otherDispatchProps.updateCustomGasPrice(gasPrice),
|
|
|
|
},
|
|
|
|
cancelAndClose: () => {
|
|
|
|
dispatchCancelAndClose();
|
|
|
|
},
|
|
|
|
disableSave:
|
|
|
|
insufficientBalance ||
|
|
|
|
(isSpeedUp && customGasPrice === 0) ||
|
|
|
|
customGasLimit < Number(MIN_GAS_LIMIT_DEC),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export default connect(
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToProps,
|
|
|
|
mergeProps,
|
|
|
|
)(GasModalPageContainer);
|
|
|
|
|
|
|
|
function isConfirm(state) {
|
|
|
|
return Boolean(Object.keys(state.confirmTransaction.txData).length);
|
|
|
|
}
|
|
|
|
|
|
|
|
function calcCustomGasPrice(customGasPriceInHex) {
|
|
|
|
return Number(hexWEIToDecGWEI(customGasPriceInHex));
|
|
|
|
}
|
|
|
|
|
|
|
|
function calcCustomGasLimit(customGasLimitInHex) {
|
|
|
|
return parseInt(customGasLimitInHex, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
function sumHexWEIsToRenderableEth(hexWEIs) {
|
|
|
|
const hexWEIsSum = hexWEIs.filter(Boolean).reduce(addHexes);
|
|
|
|
return formatETHFee(
|
|
|
|
getValueFromWeiHex({
|
|
|
|
value: hexWEIsSum,
|
|
|
|
toCurrency: 'ETH',
|
|
|
|
numberOfDecimals: 6,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function subtractHexWEIsFromRenderableEth(aHexWEI, bHexWEI) {
|
|
|
|
return formatETHFee(subtractHexWEIsToDec(aHexWEI, bHexWEI));
|
|
|
|
}
|