Merge pull request #379 from poanetwork/vb-set-custom-nonce
Ability to set custom tx nonce
This commit is contained in:
commit
d1611a4e04
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- [#379](https://github.com/poanetwork/nifty-wallet/pull/379) - (Feature) Ability to set custom nonce of tx
|
||||||
- [#377](https://github.com/poanetwork/nifty-wallet/pull/377) - (Fix) Sign message screen: do not decode message if it is not hex encoded
|
- [#377](https://github.com/poanetwork/nifty-wallet/pull/377) - (Fix) Sign message screen: do not decode message if it is not hex encoded
|
||||||
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - (Fix) notifications order in batch requests
|
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - (Fix) notifications order in batch requests
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,8 @@ class TransactionController extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
async updateAndApproveTransaction (txMeta) {
|
async updateAndApproveTransaction (txMeta) {
|
||||||
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
||||||
await this.approveTransaction(txMeta.id)
|
const customNonce = txMeta.txParams.nonce
|
||||||
|
await this.approveTransaction(txMeta.id, customNonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -301,7 +302,7 @@ class TransactionController extends EventEmitter {
|
||||||
if any of these steps fails the tx status will be set to failed
|
if any of these steps fails the tx status will be set to failed
|
||||||
@param txId {number} - the tx's Id
|
@param txId {number} - the tx's Id
|
||||||
*/
|
*/
|
||||||
async approveTransaction (txId) {
|
async approveTransaction (txId, customNonce) {
|
||||||
let nonceLock
|
let nonceLock
|
||||||
try {
|
try {
|
||||||
// approve
|
// approve
|
||||||
|
@ -315,7 +316,7 @@ class TransactionController extends EventEmitter {
|
||||||
// if txMeta has lastGasPrice then it is a retry at same nonce with higher
|
// 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
|
// gas price transaction and their for the nonce should not be calculated
|
||||||
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
|
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
|
||||||
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
|
txMeta.txParams.nonce = customNonce || ethUtil.addHexPrefix(nonce.toString(16))
|
||||||
// add nonce debugging information to txMeta
|
// add nonce debugging information to txMeta
|
||||||
txMeta.nonceDetails = nonceLock.nonceDetails
|
txMeta.nonceDetails = nonceLock.nonceDetails
|
||||||
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
||||||
|
|
|
@ -1,48 +1,35 @@
|
||||||
const inherits = require('util').inherits
|
import React from 'react'
|
||||||
const PersistentForm = require('../../../lib/persistent-form')
|
import PersistentForm from '../../../lib/persistent-form'
|
||||||
const h = require('react-hyperscript')
|
import { connect } from 'react-redux'
|
||||||
const connect = require('react-redux').connect
|
import actions from '../../../../ui/app/actions'
|
||||||
const actions = require('../../../../ui/app/actions')
|
import {
|
||||||
const {
|
|
||||||
numericBalance,
|
numericBalance,
|
||||||
isHex,
|
isHex,
|
||||||
normalizeEthStringToWei,
|
normalizeEthStringToWei,
|
||||||
isInvalidChecksumAddress,
|
isInvalidChecksumAddress,
|
||||||
isValidAddress,
|
isValidAddress,
|
||||||
} = require('../../util')
|
} from '../../util'
|
||||||
const EnsInput = require('../ens-input')
|
import EnsInput from '../ens-input'
|
||||||
const ethUtil = require('ethereumjs-util')
|
import ethUtil from 'ethereumjs-util'
|
||||||
import SendProfile from './send-profile'
|
import SendProfile from './send-profile'
|
||||||
import SendHeader from './send-header'
|
import SendHeader from './send-header'
|
||||||
import ErrorComponent from '../error'
|
import ErrorComponent from '../error'
|
||||||
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
import ToastComponent from '../toast'
|
import ToastComponent from '../toast'
|
||||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
const optionalDataLabelStyle = {
|
||||||
const accounts = getMetaMaskAccounts(state)
|
background: '#ffffff',
|
||||||
const result = {
|
color: '#333333',
|
||||||
address: state.metamask.selectedAddress,
|
marginTop: '16px',
|
||||||
accounts,
|
marginBottom: '16px',
|
||||||
identities: state.metamask.identities,
|
}
|
||||||
warning: state.appState.warning,
|
const optionalDataValueStyle = {
|
||||||
network: state.metamask.network,
|
width: '100%',
|
||||||
addressBook: state.metamask.addressBook,
|
resize: 'none',
|
||||||
}
|
}
|
||||||
|
|
||||||
result.error = result.warning && result.warning.split('.')[0]
|
class SendTransactionScreen extends PersistentForm {
|
||||||
result.account = result.accounts[result.address]
|
render () {
|
||||||
result.balance = result.account ? numericBalance(result.account.balance) : null
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(SendTransactionScreen, PersistentForm)
|
|
||||||
function SendTransactionScreen () {
|
|
||||||
PersistentForm.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTransactionScreen.prototype.render = function () {
|
|
||||||
this.persistentFormParentId = 'send-tx-form'
|
this.persistentFormParentId = 'send-tx-form'
|
||||||
|
|
||||||
const props = this.props
|
const props = this.props
|
||||||
|
@ -54,114 +41,105 @@ SendTransactionScreen.prototype.render = function () {
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="send-screen flex-column flex-grow">
|
||||||
|
<ToastComponent isSuccess={false} />
|
||||||
|
<SendProfile/>
|
||||||
|
|
||||||
h('.send-screen.flex-column.flex-grow', [
|
<SendHeader
|
||||||
|
title= "Send Transaction"
|
||||||
|
/>
|
||||||
|
|
||||||
h(ToastComponent, {
|
<ErrorComponent
|
||||||
isSuccess: false,
|
error={error}
|
||||||
}),
|
/>
|
||||||
|
|
||||||
//
|
<section className="flex-row flex-center">
|
||||||
// Sender Profile
|
<EnsInput
|
||||||
//
|
name="address"
|
||||||
|
placeholder="Recipient Address"
|
||||||
|
onChange={this.recipientDidChange.bind(this)}
|
||||||
|
network={network}
|
||||||
|
identities={identities}
|
||||||
|
addressBook={addressBook}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
h(SendProfile),
|
<section className="flex-row flex-center">
|
||||||
|
|
||||||
//
|
<input className="large-input"
|
||||||
// Send Header
|
name= "amount"
|
||||||
//
|
placeholder= "Amount"
|
||||||
|
type= "number"
|
||||||
h(SendHeader, {
|
style= {{
|
||||||
title: 'Send Transaction',
|
|
||||||
}),
|
|
||||||
|
|
||||||
// error message
|
|
||||||
h(ErrorComponent, {
|
|
||||||
error,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 'to' field
|
|
||||||
h('section.flex-row.flex-center', [
|
|
||||||
h(EnsInput, {
|
|
||||||
name: 'address',
|
|
||||||
placeholder: 'Recipient Address',
|
|
||||||
onChange: this.recipientDidChange.bind(this),
|
|
||||||
network,
|
|
||||||
identities,
|
|
||||||
addressBook,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// 'amount' and send button
|
|
||||||
h('section.flex-row.flex-center', [
|
|
||||||
|
|
||||||
h('input.large-input', {
|
|
||||||
name: 'amount',
|
|
||||||
placeholder: 'Amount',
|
|
||||||
type: 'number',
|
|
||||||
style: {
|
|
||||||
marginRight: '6px',
|
marginRight: '6px',
|
||||||
},
|
}}
|
||||||
dataset: {
|
dataset={{
|
||||||
persistentFormid: 'tx-amount',
|
persistentFormid: 'tx-amount',
|
||||||
},
|
}}
|
||||||
}),
|
/>
|
||||||
|
|
||||||
h('button', {
|
<button
|
||||||
onClick: this.onSubmit.bind(this),
|
onClick={this.onSubmit.bind(this)}>
|
||||||
}, 'Next'),
|
Next
|
||||||
|
</button>
|
||||||
|
|
||||||
]),
|
</section>
|
||||||
|
|
||||||
//
|
<h3 className="flex-center"
|
||||||
// Optional Fields
|
style={optionalDataLabelStyle}
|
||||||
//
|
>
|
||||||
h('h3.flex-center', {
|
Transaction Data (optional)
|
||||||
style: {
|
</h3>
|
||||||
background: '#ffffff',
|
|
||||||
color: '#333333',
|
|
||||||
marginTop: '16px',
|
|
||||||
marginBottom: '16px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
'Transaction Data (optional)',
|
|
||||||
]),
|
|
||||||
|
|
||||||
// 'data' field
|
<section className="flex-column flex-center">
|
||||||
h('section.flex-column.flex-center', [
|
<input className="large-input"
|
||||||
h('input.large-input', {
|
name= "txData"
|
||||||
name: 'txData',
|
placeholder= "e.g. 0x01234"
|
||||||
placeholder: '0x01234',
|
style={optionalDataValueStyle}
|
||||||
style: {
|
dataset={{
|
||||||
width: '100%',
|
|
||||||
resize: 'none',
|
|
||||||
},
|
|
||||||
dataset: {
|
|
||||||
persistentFormid: 'tx-data',
|
persistentFormid: 'tx-data',
|
||||||
},
|
}}
|
||||||
}),
|
/>
|
||||||
]),
|
</section>
|
||||||
])
|
|
||||||
|
<h3 className="flex-center"
|
||||||
|
style={optionalDataLabelStyle}
|
||||||
|
>
|
||||||
|
Custom nonce (optional)
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<section className="flex-column flex-center">
|
||||||
|
<input className="large-input"
|
||||||
|
name= "txCustomNonce"
|
||||||
|
type= "number"
|
||||||
|
placeholder= "e.g. 42"
|
||||||
|
style={optionalDataValueStyle}
|
||||||
|
dataset={{
|
||||||
|
persistentFormid: 'tx-custom-nonce',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.componentWillUnmount = function () {
|
componentWillUnmount () {
|
||||||
this.props.dispatch(actions.displayWarning(''))
|
this.props.dispatch(actions.displayWarning(''))
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.navigateToAccounts = function (event) {
|
navigateToAccounts (event) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
this.props.dispatch(actions.showAccountsPage())
|
this.props.dispatch(actions.showAccountsPage())
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
|
recipientDidChange (recipient, nickname) {
|
||||||
this.setState({
|
this.setState({
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
nickname: nickname,
|
nickname: nickname,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.onSubmit = function () {
|
onSubmit () {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
let recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
let recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
||||||
let nickname = state.nickname || ' '
|
let nickname = state.nickname || ' '
|
||||||
|
@ -193,6 +171,7 @@ SendTransactionScreen.prototype.onSubmit = function () {
|
||||||
|
|
||||||
const value = normalizeEthStringToWei(input)
|
const value = normalizeEthStringToWei(input)
|
||||||
const txData = document.querySelector('input[name="txData"]').value
|
const txData = document.querySelector('input[name="txData"]').value
|
||||||
|
const txCustomNonce = document.querySelector('input[name="txCustomNonce"]').value
|
||||||
const balance = this.props.balance
|
const balance = this.props.balance
|
||||||
|
|
||||||
if (value.gt(balance)) {
|
if (value.gt(balance)) {
|
||||||
|
@ -231,6 +210,28 @@ SendTransactionScreen.prototype.onSubmit = function () {
|
||||||
|
|
||||||
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
|
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
|
||||||
if (txData) txParams.data = txData
|
if (txData) txParams.data = txData
|
||||||
|
if (txCustomNonce) txParams.nonce = '0x' + parseInt(txCustomNonce, 10).toString(16)
|
||||||
|
|
||||||
this.props.dispatch(actions.signTx(txParams))
|
this.props.dispatch(actions.signTx(txParams))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
|
const result = {
|
||||||
|
address: state.metamask.selectedAddress,
|
||||||
|
accounts,
|
||||||
|
identities: state.metamask.identities,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
network: state.metamask.network,
|
||||||
|
addressBook: state.metamask.addressBook,
|
||||||
|
}
|
||||||
|
|
||||||
|
result.error = result.warning && result.warning.split('.')[0]
|
||||||
|
result.account = result.accounts[result.address]
|
||||||
|
result.balance = result.account ? numericBalance(result.account.balance) : null
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
||||||
|
|
|
@ -345,7 +345,6 @@ describe('Transaction Controller', function () {
|
||||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||||
gasPrice: '0x77359400',
|
gasPrice: '0x77359400',
|
||||||
gas: '0x7b0d',
|
gas: '0x7b0d',
|
||||||
nonce: '0x4b',
|
|
||||||
},
|
},
|
||||||
metamaskNetworkId: currentNetworkId,
|
metamaskNetworkId: currentNetworkId,
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,6 @@ const actions = {
|
||||||
cancelPersonalMsg,
|
cancelPersonalMsg,
|
||||||
signTypedMsg,
|
signTypedMsg,
|
||||||
cancelTypedMsg,
|
cancelTypedMsg,
|
||||||
sendTx: sendTx,
|
|
||||||
signTx: signTx,
|
signTx: signTx,
|
||||||
signTokenTx: signTokenTx,
|
signTokenTx: signTokenTx,
|
||||||
updateTransaction,
|
updateTransaction,
|
||||||
|
@ -1198,22 +1197,6 @@ function clearSend () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function sendTx (txData) {
|
|
||||||
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
|
|
||||||
return (dispatch) => {
|
|
||||||
log.debug(`actions calling background.approveTransaction`)
|
|
||||||
background.approveTransaction(txData.id, (err) => {
|
|
||||||
if (err) {
|
|
||||||
err = err.message || err.error || err
|
|
||||||
dispatch(actions.txError(err))
|
|
||||||
return log.error(err)
|
|
||||||
}
|
|
||||||
dispatch(actions.completedTx(txData.id))
|
|
||||||
dispatch(actions.closeCurrentNotificationWindow())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function signTokenTx (tokenAddress, toAddress, amount, txData, confTxScreenParams) {
|
function signTokenTx (tokenAddress, toAddress, amount, txData, confTxScreenParams) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
|
|
Loading…
Reference in New Issue