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
feature/default_network_editable
ricky 5 years ago committed by Dan J Miller
parent 970e90ea70
commit 5f254f7325
  1. 16
      app/_locales/en/messages.json
  2. 22
      app/scripts/controllers/preferences.js
  3. 9
      app/scripts/controllers/transactions/index.js
  4. 16
      app/scripts/metamask-controller.js
  5. 2
      test/e2e/threebox.spec.js
  6. 7
      ui/app/components/app/confirm-page-container/confirm-detail-row/index.scss
  7. 4
      ui/app/components/app/transaction-list-item/transaction-list-item.container.js
  8. 14
      ui/app/ducks/metamask/metamask.js
  9. 89
      ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
  10. 33
      ui/app/pages/confirm-transaction-base/confirm-transaction-base.container.js
  11. 20
      ui/app/pages/confirm-transaction-base/tests/confirm-transaction-base.container.test.js
  12. 29
      ui/app/pages/settings/advanced-tab/advanced-tab.component.js
  13. 4
      ui/app/pages/settings/advanced-tab/advanced-tab.container.js
  14. 4
      ui/app/pages/settings/advanced-tab/tests/advanced-tab-component.test.js
  15. 3
      ui/app/pages/settings/advanced-tab/tests/advanced-tab-container.test.js
  16. 10
      ui/app/selectors/selectors.js
  17. 55
      ui/app/store/actions.js

@ -195,6 +195,18 @@
"blockiesIdenticon": {
"message": "Use Blockies Identicon"
},
"nonceField": {
"message": "Customize transaction nonce"
},
"nonceFieldPlaceholder": {
"message": "Automatically calculate"
},
"nonceFieldHeading": {
"message": "Custom Nonce"
},
"nonceFieldDescription": {
"message": "Turn this on to change the nonce (transaction number) on confirmation screens. This is an advanced feature, use cautiously."
},
"browserNotSupported": {
"message": "Your Browser is not supported..."
},
@ -851,6 +863,10 @@
"next": {
"message": "Next"
},
"nextNonceWarning": {
"message": "Nonce is higher than suggested nonce of $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"noAddressForName": {
"message": "No address has been set for this name."
},

@ -17,6 +17,7 @@ class PreferencesController {
* @property {object} store.accountTokens The tokens stored per account and then per network type
* @property {object} store.assetImages Contains assets objects related to assets added
* @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
* @property {boolean} store.useNonceField The users preference for nonce field within the UI
* @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
* user wishes to see that feature.
*
@ -35,6 +36,7 @@ class PreferencesController {
tokens: [],
suggestedTokens: {},
useBlockie: false,
useNonceField: false,
// WARNING: Do not use feature flags for security-sensitive things.
// Feature flag toggling is available in the global namespace
@ -89,6 +91,16 @@ class PreferencesController {
this.store.updateState({ useBlockie: val })
}
/**
* Setter for the `useNonceField` property
*
* @param {boolean} val Whether or not the user prefers to set nonce
*
*/
setUseNonceField (val) {
this.store.updateState({ useNonceField: val })
}
/**
* Setter for the `participateInMetaMetrics` property
*
@ -204,6 +216,16 @@ class PreferencesController {
return this.store.getState().useBlockie
}
/**
* Getter for the `getUseNonceField` property
*
* @returns {boolean} this.store.getUseNonceField
*
*/
getUseNonceField () {
return this.store.getState().useNonceField
}
/**
* Setter for the `currentLocale` property
*

@ -373,14 +373,21 @@ class TransactionController extends EventEmitter {
const txMeta = this.txStateManager.getTx(txId)
const fromAddress = txMeta.txParams.from
// wait for a nonce
let { customNonceValue = null } = txMeta
customNonceValue = Number(customNonceValue)
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams
// if txMeta has lastGasPrice then it is a retry at same nonce with higher
// gas price transaction and their for the nonce should not be calculated
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
const customOrNonce = customNonceValue || nonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(customOrNonce.toString(16))
// add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails
if (customNonceValue) {
txMeta.nonceDetails.customNonceValue = customNonceValue
}
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
// sign transaction
const rawTx = await this.signTransaction(txId)

@ -440,6 +440,7 @@ module.exports = class MetamaskController extends EventEmitter {
getState: (cb) => cb(null, this.getState()),
setCurrentCurrency: this.setCurrentCurrency.bind(this),
setUseBlockie: this.setUseBlockie.bind(this),
setUseNonceField: this.setUseNonceField.bind(this),
setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this),
setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this),
setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this),
@ -519,6 +520,7 @@ module.exports = class MetamaskController extends EventEmitter {
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
isNonceTaken: nodeify(txController.isNonceTaken, txController),
estimateGas: nodeify(this.estimateGas, this),
getPendingNonce: nodeify(this.getPendingNonce, this),
// messageManager
signMessage: nodeify(this.signMessage, this),
@ -1727,6 +1729,20 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
/**
* Sets whether or not to use the nonce field.
* @param {boolean} val - True for nonce field, false for not nonce field.
* @param {Function} cb - A callback function called when complete.
*/
setUseNonceField (val, cb) {
try {
this.preferencesController.setUseNonceField(val)
cb(null)
} catch (err) {
cb(err)
}
}
/**
* Sets whether or not the user will have usage data tracked with MetaMetrics
* @param {boolean} bool - True for users that wish to opt-in, false for users that wish to remain out.

@ -116,7 +116,7 @@ describe('MetaMask', function () {
await advancedButton.click()
const threeBoxToggle = await findElements(driver, By.css('.toggle-button'))
const threeBoxToggleButton = await threeBoxToggle[3].findElement(By.css('div'))
const threeBoxToggleButton = await threeBoxToggle[4].findElement(By.css('div'))
await threeBoxToggleButton.click()
})

@ -47,4 +47,11 @@
.advanced-gas-inputs__gas-edit-rows {
margin-bottom: 16px;
}
.custom-nonce-input {
input {
width: 90px;
font-size: 1rem;
}
}
}

@ -21,10 +21,10 @@ const mapStateToProps = (state, ownProps) => {
const { showFiatInTestnets } = preferencesSelector(state)
const isMainnet = getIsMainnet(state)
const { transactionGroup: { primaryTransaction } = {} } = ownProps
const { txParams: { gas: gasLimit, gasPrice, data, to } = {} } = primaryTransaction
const { txParams: { gas: gasLimit, gasPrice, data } = {}, transactionCategory } = primaryTransaction
const selectedAddress = getSelectedAddress(state)
const selectedAccountBalance = accounts[selectedAddress].balance
const isDeposit = selectedAddress === to
const isDeposit = transactionCategory === 'incoming'
const selectRpcInfo = frequentRpcListDetail.find(rpcInfo => rpcInfo.rpcUrl === provider.rpcTarget)
const { rpcPrefs } = selectRpcInfo || {}

@ -25,6 +25,7 @@ function reduceMetamask (state, action) {
tokenExchangeRates: {},
tokens: [],
pendingTokens: {},
customNonceValue: '',
send: {
gasLimit: null,
gasPrice: null,
@ -57,6 +58,7 @@ function reduceMetamask (state, action) {
knownMethodData: {},
participateInMetaMetrics: null,
metaMetricsSendCount: 0,
nextNonce: null,
}, state.metamask)
switch (action.type) {
@ -188,7 +190,10 @@ function reduceMetamask (state, action) {
gasLimit: action.value,
},
})
case actions.UPDATE_CUSTOM_NONCE:
return extend(metamaskState, {
customNonceValue: action.value,
})
case actions.UPDATE_GAS_PRICE:
return extend(metamaskState, {
send: {
@ -412,6 +417,13 @@ function reduceMetamask (state, action) {
})
}
case actions.SET_NEXT_NONCE: {
console.log('action.value', action.value)
return extend(metamaskState, {
nextNonce: action.value,
})
}
default:
return metamaskState

@ -15,6 +15,7 @@ import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../helpers/constants/transa
import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../helpers/constants/common'
import AdvancedGasInputs from '../../components/app/gas-customization/advanced-gas-inputs'
import TextField from '../../components/ui/text-field'
export default class ConfirmTransactionBase extends Component {
static contextTypes = {
@ -50,6 +51,9 @@ export default class ConfirmTransactionBase extends Component {
isTxReprice: PropTypes.bool,
methodData: PropTypes.object,
nonce: PropTypes.string,
useNonceField: PropTypes.bool,
customNonceValue: PropTypes.string,
updateCustomNonce: PropTypes.func,
assetImage: PropTypes.string,
sendTransaction: PropTypes.func,
showCustomizeGasModal: PropTypes.func,
@ -96,11 +100,14 @@ export default class ConfirmTransactionBase extends Component {
insufficientBalance: PropTypes.bool,
hideFiatConversion: PropTypes.bool,
transactionCategory: PropTypes.string,
getNextNonce: PropTypes.func,
nextNonce: PropTypes.number,
}
state = {
submitting: false,
submitError: null,
submitWarning: '',
}
componentDidUpdate (prevProps) {
@ -109,11 +116,21 @@ export default class ConfirmTransactionBase extends Component {
showTransactionConfirmedModal,
history,
clearConfirmTransaction,
nextNonce,
customNonceValue,
} = this.props
const { transactionStatus: prevTxStatus } = prevProps
const statusUpdated = transactionStatus !== prevTxStatus
const txDroppedOrConfirmed = transactionStatus === DROPPED_STATUS || transactionStatus === CONFIRMED_STATUS
if (nextNonce !== prevProps.nextNonce || customNonceValue !== prevProps.customNonceValue) {
if (customNonceValue > nextNonce) {
this.setState({ submitWarning: this.context.t('nextNonceWarning', [nextNonce]) })
} else {
this.setState({ submitWarning: '' })
}
}
if (statusUpdated && txDroppedOrConfirmed) {
showTransactionConfirmedModal({
onSubmit: () => {
@ -204,11 +221,16 @@ export default class ConfirmTransactionBase extends Component {
hexTransactionFee,
hexTransactionTotal,
hideDetails,
useNonceField,
customNonceValue,
updateCustomNonce,
advancedInlineGasShown,
customGas,
insufficientBalance,
updateGasAndCalculate,
hideFiatConversion,
nextNonce,
getNextNonce,
} = this.props
if (hideDetails) {
@ -240,7 +262,7 @@ export default class ConfirmTransactionBase extends Component {
: null
}
</div>
<div>
<div className={useNonceField ? 'confirm-page-container-content__gas-fee' : null}>
<ConfirmDetailRow
label="Total"
value={hexTransactionTotal}
@ -251,6 +273,31 @@ export default class ConfirmTransactionBase extends Component {
primaryValueTextColor="#2f9ae0"
/>
</div>
{useNonceField ? <div>
<div className="confirm-detail-row">
<div className="confirm-detail-row__label">
{ this.context.t('nonceFieldHeading') }
</div>
<div className="custom-nonce-input">
<TextField
type="number"
min="0"
placeholder={ this.context.t('nonceFieldPlaceholder') }
onChange={({ target: { value } }) => {
if (!value.length || Number(value) < 0) {
updateCustomNonce('')
} else {
updateCustomNonce(String(Math.floor(value)))
}
getNextNonce()
}}
fullWidth
margin="dense"
value={customNonceValue || nextNonce || ''}
/>
</div>
</div>
</div> : null}
</div>
)
)
@ -347,7 +394,17 @@ export default class ConfirmTransactionBase extends Component {
handleCancel () {
const { metricsEvent } = this.context
const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction, actionKey, txData: { origin }, methodData = {} } = this.props
const {
onCancel,
txData,
cancelTransaction,
history,
clearConfirmTransaction,
actionKey,
txData: { origin },
methodData = {},
updateCustomNonce,
} = this.props
metricsEvent({
eventOpts: {
@ -361,6 +418,7 @@ export default class ConfirmTransactionBase extends Component {
origin,
},
})
updateCustomNonce('')
if (onCancel) {
onCancel(txData)
} else {
@ -374,7 +432,19 @@ export default class ConfirmTransactionBase extends Component {
handleSubmit () {
const { metricsEvent } = this.context
const { txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, onSubmit, actionKey, metaMetricsSendCount = 0, setMetaMetricsSendCount, methodData = {} } = this.props
const {
txData: { origin },
sendTransaction,
clearConfirmTransaction,
txData,
history,
onSubmit,
actionKey,
metaMetricsSendCount = 0,
setMetaMetricsSendCount,
methodData = {},
updateCustomNonce,
} = this.props
const { submitting } = this.state
if (submitting) {
@ -406,6 +476,7 @@ export default class ConfirmTransactionBase extends Component {
this.setState({
submitting: false,
})
updateCustomNonce('')
})
} else {
sendTransaction(txData)
@ -415,6 +486,7 @@ export default class ConfirmTransactionBase extends Component {
submitting: false,
}, () => {
history.push(DEFAULT_ROUTE)
updateCustomNonce('')
})
})
.catch(error => {
@ -493,7 +565,7 @@ export default class ConfirmTransactionBase extends Component {
}
componentDidMount () {
const { txData: { origin, id } = {}, cancelTransaction } = this.props
const { txData: { origin, id } = {}, cancelTransaction, getNextNonce } = this.props
const { metricsEvent } = this.context
metricsEvent({
eventOpts: {
@ -521,6 +593,8 @@ export default class ConfirmTransactionBase extends Component {
cancelTransaction({ id })
}
}
getNextNonce()
}
render () {
@ -543,12 +617,13 @@ export default class ConfirmTransactionBase extends Component {
contentComponent,
onEdit,
nonce,
customNonceValue,
assetImage,
warning,
unapprovedTxCount,
transactionCategory,
} = this.props
const { submitting, submitError } = this.state
const { submitting, submitError, submitWarning } = this.state
const { name } = methodData
const { valid, errorKey } = this.getErrorKey()
@ -572,13 +647,13 @@ export default class ConfirmTransactionBase extends Component {
detailsComponent={this.renderDetails()}
dataComponent={this.renderData()}
contentComponent={contentComponent}
nonce={nonce}
nonce={customNonceValue || nonce}
unapprovedTxCount={unapprovedTxCount}
assetImage={assetImage}
identiconAddress={identiconAddress}
errorMessage={errorMessage || submitError}
errorKey={propsErrorKey || errorKey}
warning={warning}
warning={warning || submitWarning}
totalTx={totalTx}
positionOfCurrentTx={positionOfCurrentTx}
nextTxId={nextTxId}

@ -8,7 +8,17 @@ import {
clearConfirmTransaction,
} from '../../ducks/confirm-transaction/confirm-transaction.duck'
import { clearSend, cancelTx, cancelTxs, updateAndApproveTx, showModal, setMetaMetricsSendCount, updateTransaction } from '../../store/actions'
import {
updateCustomNonce,
clearSend,
cancelTx,
cancelTxs,
updateAndApproveTx,
showModal,
setMetaMetricsSendCount,
updateTransaction,
getNextNonce,
} from '../../store/actions'
import {
INSUFFICIENT_FUNDS_ERROR_KEY,
GAS_LIMIT_TOO_LOW_ERROR_KEY,
@ -18,7 +28,7 @@ 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, getAdvancedInlineGasShown, preferencesSelector, getIsMainnet, getKnownMethodData } from '../../selectors/selectors'
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) => {
@ -28,6 +38,12 @@ const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
}
}, {})
let customNonceValue = ''
const customNonceMerge = txData => customNonceValue ? ({
...txData,
customNonceValue,
}) : txData
const mapStateToProps = (state, ownProps) => {
const { toAddress: propsToAddress, match: { params = {} } } = ownProps
const { id: paramsTransactionId } = params
@ -45,6 +61,7 @@ const mapStateToProps = (state, ownProps) => {
network,
unapprovedTxs,
metaMetricsSendCount,
nextNonce,
} = metamask
const {
tokenData,
@ -146,16 +163,23 @@ const mapStateToProps = (state, ownProps) => {
gasPrice,
},
advancedInlineGasShown: getAdvancedInlineGasShown(state),
useNonceField: getUseNonceField(state),
customNonceValue: getCustomNonceValue(state),
insufficientBalance,
hideSubtitle: (!isMainnet && !showFiatInTestnets),
hideFiatConversion: (!isMainnet && !showFiatInTestnets),
metaMetricsSendCount,
transactionCategory,
nextNonce,
}
}
const mapDispatchToProps = dispatch => {
export const mapDispatchToProps = dispatch => {
return {
updateCustomNonce: value => {
customNonceValue = value
dispatch(updateCustomNonce(value))
},
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
clearSend: () => dispatch(clearSend()),
showTransactionConfirmedModal: ({ onSubmit }) => {
@ -172,8 +196,9 @@ const mapDispatchToProps = dispatch => {
},
cancelTransaction: ({ id }) => dispatch(cancelTx({ id })),
cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)),
sendTransaction: txData => dispatch(updateAndApproveTx(txData)),
sendTransaction: txData => dispatch(updateAndApproveTx(customNonceMerge(txData))),
setMetaMetricsSendCount: val => dispatch(setMetaMetricsSendCount(val)),
getNextNonce: () => dispatch(getNextNonce()),
}
}

@ -0,0 +1,20 @@
import assert from 'assert'
import { mapDispatchToProps } from '../confirm-transaction-base.container'
describe('Confirm Transaction Base Container', () => {
it('should map dispatch to props correctly', () => {
const props = mapDispatchToProps(() => 'mockDispatch')
assert.ok(typeof props.updateCustomNonce === 'function')
assert.ok(typeof props.clearConfirmTransaction === 'function')
assert.ok(typeof props.clearSend === 'function')
assert.ok(typeof props.showTransactionConfirmedModal === 'function')
assert.ok(typeof props.showCustomizeGasModal === 'function')
assert.ok(typeof props.updateGasAndCalculate === 'function')
assert.ok(typeof props.showRejectTransactionsConfirmationModal === 'function')
assert.ok(typeof props.cancelTransaction === 'function')
assert.ok(typeof props.cancelAllTransactions === 'function')
assert.ok(typeof props.sendTransaction === 'function')
assert.ok(typeof props.setMetaMetricsSendCount === 'function')
})
})

@ -14,6 +14,8 @@ export default class AdvancedTab extends PureComponent {
}
static propTypes = {
setUseNonceField: PropTypes.func,
useNonceField: PropTypes.bool,
setHexDataFeatureFlag: PropTypes.func,
setRpcTarget: PropTypes.func,
displayWarning: PropTypes.func,
@ -212,6 +214,32 @@ export default class AdvancedTab extends PureComponent {
)
}
renderUseNonceOptIn () {
const { t } = this.context
const { useNonceField, setUseNonceField } = this.props
return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<span>{ this.context.t('nonceField') }</span>
<div className="settings-page__content-description">
{ t('nonceFieldDescription') }
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<ToggleButton
value={useNonceField}
onToggle={value => setUseNonceField(!value)}
offLabel={t('off')}
onLabel={t('on')}
/>
</div>
</div>
</div>
)
}
renderAutoLogoutTimeLimit () {
const { t } = this.context
const {
@ -311,6 +339,7 @@ export default class AdvancedTab extends PureComponent {
{ this.renderAdvancedGasInputInline() }
{ this.renderHexDataOptIn() }
{ this.renderShowConversionInTestnets() }
{ this.renderUseNonceOptIn() }
{ this.renderAutoLogoutTimeLimit() }
{ this.renderThreeBoxControl() }
</div>

@ -11,6 +11,7 @@ import {
setAutoLogoutTimeLimit,
setThreeBoxSyncingPermission,
turnThreeBoxSyncingOnAndInitialize,
setUseNonceField,
} from '../../../store/actions'
import {preferencesSelector} from '../../../selectors/selectors'
@ -23,6 +24,7 @@ export const mapStateToProps = state => {
} = {},
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
} = metamask
const { showFiatInTestnets, autoLogoutTimeLimit } = preferencesSelector(state)
@ -34,6 +36,7 @@ export const mapStateToProps = state => {
autoLogoutTimeLimit,
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
}
}
@ -44,6 +47,7 @@ export const mapDispatchToProps = dispatch => {
displayWarning: warning => dispatch(displayWarning(warning)),
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
setAdvancedInlineGasFeatureFlag: shouldShow => dispatch(setFeatureFlag('advancedInlineGas', shouldShow)),
setUseNonceField: value => dispatch(setUseNonceField(value)),
setShowFiatConversionOnTestnetsPreference: value => {
return dispatch(setShowFiatConversionOnTestnetsPreference(value))
},

@ -16,7 +16,7 @@ describe('AdvancedTab Component', () => {
}
)
assert.equal(root.find('.settings-page__content-row').length, 8)
assert.equal(root.find('.settings-page__content-row').length, 9)
})
it('should update autoLogoutTimeLimit', () => {
@ -32,7 +32,7 @@ describe('AdvancedTab Component', () => {
}
)
const autoTimeout = root.find('.settings-page__content-row').at(6)
const autoTimeout = root.find('.settings-page__content-row').at(7)
const textField = autoTimeout.find(TextField)
textField.props().onChange({ target: { value: 1440 } })

@ -17,6 +17,7 @@ const defaultState = {
},
threeBoxSyncingAllowed: false,
threeBoxDisabled: false,
useNonceField: false,
},
}
@ -31,6 +32,7 @@ describe('AdvancedTab Container', () => {
autoLogoutTimeLimit: 0,
threeBoxSyncingAllowed: false,
threeBoxDisabled: false,
useNonceField: false,
}
assert.deepEqual(props, expected)
@ -46,5 +48,6 @@ describe('AdvancedTab Container', () => {
assert.ok(typeof props.setAdvancedInlineGasFeatureFlag === 'function')
assert.ok(typeof props.setShowFiatConversionOnTestnetsPreference === 'function')
assert.ok(typeof props.setAutoLogoutTimeLimit === 'function')
assert.ok(typeof props.setUseNonceField === 'function')
})
})

@ -45,6 +45,8 @@ const selectors = {
getNetworkIdentifier,
isBalanceCached,
getAdvancedInlineGasShown,
getUseNonceField,
getCustomNonceValue,
getIsMainnet,
getCurrentNetworkId,
getSelectedAsset,
@ -341,6 +343,14 @@ function getAdvancedInlineGasShown (state) {
return Boolean(state.metamask.featureFlags.advancedInlineGas)
}
function getUseNonceField (state) {
return Boolean(state.metamask.useNonceField)
}
function getCustomNonceValue (state) {
return String(state.metamask.customNonceValue)
}
function getMetaMetricState (state) {
return {
network: getCurrentNetworkId(state),

@ -178,6 +178,9 @@ var actions = {
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
updateTransactionParams,
UPDATE_TRANSACTION_PARAMS: 'UPDATE_TRANSACTION_PARAMS',
setNextNonce,
SET_NEXT_NONCE: 'SET_NEXT_NONCE',
getNextNonce,
// send screen
UPDATE_GAS_LIMIT: 'UPDATE_GAS_LIMIT',
UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE',
@ -298,6 +301,10 @@ var actions = {
SET_USE_BLOCKIE: 'SET_USE_BLOCKIE',
setUseBlockie,
SET_USE_NONCEFIELD: 'SET_USE_NONCEFIELD',
setUseNonceField,
UPDATE_CUSTOM_NONCE: 'UPDATE_CUSTOM_NONCE',
updateCustomNonce,
SET_PARTICIPATE_IN_METAMETRICS: 'SET_PARTICIPATE_IN_METAMETRICS',
SET_METAMETRICS_SEND_COUNT: 'SET_METAMETRICS_SEND_COUNT',
@ -1063,6 +1070,13 @@ function updateSendAmount (amount) {
}
}
function updateCustomNonce (value) {
return {
type: actions.UPDATE_CUSTOM_NONCE,
value: value,
}
}
function updateSendMemo (memo) {
return {
type: actions.UPDATE_SEND_MEMO,
@ -1208,6 +1222,7 @@ function updateAndApproveTx (txData) {
dispatch(actions.clearSend())
dispatch(actions.completedTx(txData.id))
dispatch(actions.hideLoadingIndication())
dispatch(actions.updateCustomNonce(''))
dispatch(closeCurrentNotificationWindow())
return txData
@ -2591,6 +2606,23 @@ function setUseBlockie (val) {
}
}
function setUseNonceField (val) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`background.setUseNonceField`)
background.setUseNonceField(val, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.displayWarning(err.message))
}
})
dispatch({
type: actions.SET_USE_NONCEFIELD,
value: val,
})
}
}
function updateCurrentLocale (key) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
@ -2888,3 +2920,26 @@ function turnThreeBoxSyncingOnAndInitialize () {
await dispatch(initializeThreeBox(true))
}
}
function setNextNonce (nextNonce) {
return {
type: actions.SET_NEXT_NONCE,
value: nextNonce,
}
}
function getNextNonce () {
return (dispatch, getState) => {
const address = getState().metamask.selectedAddress
return new Promise((resolve, reject) => {
background.getPendingNonce(address, (err, pendingNonce) => {
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(setNextNonce(pendingNonce))
resolve(pendingNonce)
})
})
}
}

Loading…
Cancel
Save