1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-23 03:36:18 +02:00
metamask-extension/ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js
ricky 5f254f7325 Add advanced setting to enable editing nonce on confirmation screens (#7089)
* Add UseNonce toggle

* Get the toggle actually working and dispatching

* Display nonce field on confirmation page

* Remove console.log

* Add placeholder

* Set customNonceValue

* Add nonce key/value to txParams

* remove customNonceValue from component state

* Use translation file and existing CSS class

* Use existing TextField component

* Remove console.log

* Fix lint nits

* Okay this sorta works?

* Move nonce toggle to advanced tab

* Set min to 0

* Wrap value in Number()

* Add customNonceMap

* Update custom nonce translation

* Update styles

* Reset CustomNonce

* Fix lint

* Get tests passing

* Add customNonceValue to defaults

* Fix test

* Fix comments

* Update tests

* Use camel case

* Ensure custom nonce can only be whole number

* Correct font size for custom nonce input

* UX improvements for custom nonce feature

* Fix advanced-tab-component tests for custom nonce changes

* Update title of nonce toggle in settings

* Remove unused locale message

* Cast custom nonce to string in confirm-transaction-base.component

* Handle string conversion and invalid values for custom nonces in handler

* Don't call getNonceLock in tx controller if there is a custom nonce

* Set nonce details for cases where nonce is customized

* Fix incorrectly use value for deciding whether to getnoncelock in approveTransaction

* Default nonceLock to empty object in approveTransaction

* Reapply use on nonceLock in cases where customNonceValue in approveTransaction.

* Show warning message if custom nonce is higher than MetaMask's next nonce

* Fix e2e test failure caused by custom nonce and 3box toggle conflict

* Update nonce warning message to include the suggested nonce

* Handle nextNonce comparison and update logic in lifecycle

* Default nonce field to suggested nonce

* Clear custom nonce on reject or confirm

* Fix bug where nonces are not shown in tx list on self sent transactions

* Ensure custom nonce is reset after tx is created in background

* Convert customNonceValue to number in approve tranasction controller

* Lint fix

* Call getNextNonce after updating custom nonce
2019-09-27 00:30:36 -04:00

288 lines
8.3 KiB
JavaScript

import { connect } from 'react-redux'
import { compose } from 'recompose'
import { withRouter } from 'react-router-dom'
import R from 'ramda'
import contractMap from 'eth-contract-metadata'
import ConfirmTransactionBase from './confirm-transaction-base.component'
import {
clearConfirmTransaction,
} from '../../ducks/confirm-transaction/confirm-transaction.duck'
import {
updateCustomNonce,
clearSend,
cancelTx,
cancelTxs,
updateAndApproveTx,
showModal,
setMetaMetricsSendCount,
updateTransaction,
getNextNonce,
} from '../../store/actions'
import {
INSUFFICIENT_FUNDS_ERROR_KEY,
GAS_LIMIT_TOO_LOW_ERROR_KEY,
} from '../../helpers/constants/error-keys'
import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'
import { isBalanceSufficient, calcGasTotal } from '../send/send.utils'
import { conversionGreaterThan } from '../../helpers/utils/conversion-util'
import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'
import { checksumAddress, addressSlicer, valuesFor } from '../../helpers/utils/util'
import { getMetaMaskAccounts, getCustomNonceValue, getUseNonceField, getAdvancedInlineGasShown, preferencesSelector, getIsMainnet, getKnownMethodData } from '../../selectors/selectors'
import { transactionFeeSelector } from '../../selectors/confirm-transaction'
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
return {
...acc,
[base.toLowerCase()]: contractMap[base],
}
}, {})
let customNonceValue = ''
const customNonceMerge = txData => customNonceValue ? ({
...txData,
customNonceValue,
}) : txData
const mapStateToProps = (state, ownProps) => {
const { toAddress: propsToAddress, match: { params = {} } } = ownProps
const { id: paramsTransactionId } = params
const { showFiatInTestnets } = preferencesSelector(state)
const isMainnet = getIsMainnet(state)
const { confirmTransaction, metamask } = state
const {
conversionRate,
identities,
addressBook,
currentCurrency,
selectedAddress,
selectedAddressTxList,
assetImages,
network,
unapprovedTxs,
metaMetricsSendCount,
nextNonce,
} = metamask
const {
tokenData,
txData,
tokenProps,
nonce,
} = confirmTransaction
const { txParams = {}, lastGasPrice, id: transactionId, transactionCategory } = txData
const transaction = R.find(({ id }) => id === (transactionId || Number(paramsTransactionId)))(selectedAddressTxList) || {}
const {
from: fromAddress,
to: txParamsToAddress,
gasPrice,
gas: gasLimit,
value: amount,
data,
} = transaction && transaction.txParams || txParams
const accounts = getMetaMaskAccounts(state)
const assetImage = assetImages[txParamsToAddress]
const { balance } = accounts[selectedAddress]
const { name: fromName } = identities[selectedAddress]
const toAddress = propsToAddress || txParamsToAddress
const toName = identities[toAddress]
? identities[toAddress].name
: (
casedContractMap[toAddress]
? casedContractMap[toAddress].name
: addressSlicer(checksumAddress(toAddress))
)
const addressBookObject = addressBook[checksumAddress(toAddress)]
const toNickname = addressBookObject ? addressBookObject.name : ''
const isTxReprice = Boolean(lastGasPrice)
const transactionStatus = transaction ? transaction.status : ''
const {
ethTransactionAmount,
ethTransactionFee,
ethTransactionTotal,
fiatTransactionAmount,
fiatTransactionFee,
fiatTransactionTotal,
hexTransactionAmount,
hexTransactionFee,
hexTransactionTotal,
} = transactionFeeSelector(state, transaction)
if (transaction && transaction.simulationFails) {
txData.simulationFails = transaction.simulationFails
}
const currentNetworkUnapprovedTxs = R.filter(
({ metamaskNetworkId }) => metamaskNetworkId === network,
unapprovedTxs,
)
const unapprovedTxCount = valuesFor(currentNetworkUnapprovedTxs).length
const insufficientBalance = !isBalanceSufficient({
amount,
gasTotal: calcGasTotal(gasLimit, gasPrice),
balance,
conversionRate,
})
const methodData = getKnownMethodData(state, data) || {}
return {
balance,
fromAddress,
fromName,
toAddress,
toName,
toNickname,
ethTransactionAmount,
ethTransactionFee,
ethTransactionTotal,
fiatTransactionAmount,
fiatTransactionFee,
fiatTransactionTotal,
hexTransactionAmount,
hexTransactionFee,
hexTransactionTotal,
txData: { ...txData, ...transaction },
tokenData,
methodData,
tokenProps,
isTxReprice,
currentCurrency,
conversionRate,
transactionStatus,
nonce,
assetImage,
unapprovedTxs,
unapprovedTxCount,
currentNetworkUnapprovedTxs,
customGas: {
gasLimit,
gasPrice,
},
advancedInlineGasShown: getAdvancedInlineGasShown(state),
useNonceField: getUseNonceField(state),
customNonceValue: getCustomNonceValue(state),
insufficientBalance,
hideSubtitle: (!isMainnet && !showFiatInTestnets),
hideFiatConversion: (!isMainnet && !showFiatInTestnets),
metaMetricsSendCount,
transactionCategory,
nextNonce,
}
}
export const mapDispatchToProps = dispatch => {
return {
updateCustomNonce: value => {
customNonceValue = value
dispatch(updateCustomNonce(value))
},
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
clearSend: () => dispatch(clearSend()),
showTransactionConfirmedModal: ({ onSubmit }) => {
return dispatch(showModal({ name: 'TRANSACTION_CONFIRMED', onSubmit }))
},
showCustomizeGasModal: ({ txData, onSubmit, validate }) => {
return dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData, onSubmit, validate }))
},
updateGasAndCalculate: (updatedTx) => {
return dispatch(updateTransaction(updatedTx))
},
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))),
setMetaMetricsSendCount: val => dispatch(setMetaMetricsSendCount(val)),
getNextNonce: () => dispatch(getNextNonce()),
}
}
const getValidateEditGas = ({ balance, conversionRate, txData }) => {
const { txParams: { value: amount } = {} } = txData
return ({ gasLimit, gasPrice }) => {
const gasTotal = getHexGasTotal({ gasLimit, gasPrice })
const hasSufficientBalance = isBalanceSufficient({
amount,
gasTotal,
balance,
conversionRate,
})
if (!hasSufficientBalance) {
return {
valid: false,
errorKey: INSUFFICIENT_FUNDS_ERROR_KEY,
}
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
{
value: MIN_GAS_LIMIT_DEC,
fromNumericBase: 'dec',
conversionRate,
},
{
value: gasLimit,
fromNumericBase: 'hex',
},
)
if (gasLimitTooLow) {
return {
valid: false,
errorKey: GAS_LIMIT_TOO_LOW_ERROR_KEY,
}
}
return {
valid: true,
}
}
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { balance, conversionRate, txData, unapprovedTxs } = stateProps
const {
cancelAllTransactions: dispatchCancelAllTransactions,
showCustomizeGasModal: dispatchShowCustomizeGasModal,
updateGasAndCalculate: dispatchUpdateGasAndCalculate,
...otherDispatchProps
} = dispatchProps
const validateEditGas = getValidateEditGas({ balance, conversionRate, txData })
return {
...stateProps,
...otherDispatchProps,
...ownProps,
showCustomizeGasModal: () => dispatchShowCustomizeGasModal({
txData,
onSubmit: customGas => dispatchUpdateGasAndCalculate(customGas),
validate: validateEditGas,
}),
cancelAllTransactions: () => dispatchCancelAllTransactions(valuesFor(unapprovedTxs)),
updateGasAndCalculate: ({ gasLimit, gasPrice }) => {
const updatedTx = {
...txData,
txParams: {
...txData.txParams,
gas: gasLimit,
gasPrice,
},
}
dispatchUpdateGasAndCalculate(updatedTx)
},
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps, mergeProps)
)(ConfirmTransactionBase)