Merge pull request #5216 from MetaMask/tx-cancel

Add createCancelTransaction method
feature/default_network_editable
Frankie 6 years ago committed by GitHub
commit 1552fe1c3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      app/scripts/controllers/transactions/enums.js
  2. 49
      app/scripts/controllers/transactions/index.js
  3. 14
      app/scripts/metamask-controller.js
  4. 25
      ui/app/actions.js

@ -0,0 +1,12 @@
const TRANSACTION_TYPE_CANCEL = 'cancel'
const TRANSACTION_TYPE_RETRY = 'retry'
const TRANSACTION_TYPE_STANDARD = 'standard'
const TRANSACTION_STATUS_APPROVED = 'approved'
module.exports = {
TRANSACTION_TYPE_CANCEL,
TRANSACTION_TYPE_RETRY,
TRANSACTION_TYPE_STANDARD,
TRANSACTION_STATUS_APPROVED,
}

@ -11,6 +11,14 @@ const txUtils = require('./lib/util')
const cleanErrorStack = require('../../lib/cleanErrorStack') const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel') const log = require('loglevel')
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker') const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
const {
TRANSACTION_TYPE_CANCEL,
TRANSACTION_TYPE_RETRY,
TRANSACTION_TYPE_STANDARD,
TRANSACTION_STATUS_APPROVED,
} = require('./enums')
const { hexToBn, bnToHex } = require('../../lib/util')
/** /**
Transaction Controller is an aggregate of sub-controllers and trackers Transaction Controller is an aggregate of sub-controllers and trackers
@ -160,7 +168,10 @@ class TransactionController extends EventEmitter {
const normalizedTxParams = txUtils.normalizeTxParams(txParams) const normalizedTxParams = txUtils.normalizeTxParams(txParams)
txUtils.validateTxParams(normalizedTxParams) txUtils.validateTxParams(normalizedTxParams)
// construct txMeta // construct txMeta
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) let txMeta = this.txStateManager.generateTxMeta({
txParams: normalizedTxParams,
type: TRANSACTION_TYPE_STANDARD,
})
this.addTx(txMeta) this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta) this.emit('newUnapprovedTx', txMeta)
@ -214,12 +225,46 @@ class TransactionController extends EventEmitter {
txParams: originalTxMeta.txParams, txParams: originalTxMeta.txParams,
lastGasPrice, lastGasPrice,
loadingDefaults: false, loadingDefaults: false,
type: TRANSACTION_TYPE_RETRY,
}) })
this.addTx(txMeta) this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta) this.emit('newUnapprovedTx', txMeta)
return txMeta return txMeta
} }
/**
* Creates a new approved transaction to attempt to cancel a previously submitted transaction. The
* new transaction contains the same nonce as the previous, is a basic ETH transfer of 0x value to
* the sender's address, and has a higher gasPrice than that of the previous transaction.
* @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
* @returns {txMeta}
*/
async createCancelTransaction (originalTxId, customGasPrice) {
const originalTxMeta = this.txStateManager.getTx(originalTxId)
const { txParams } = originalTxMeta
const { gasPrice: lastGasPrice, from, nonce } = txParams
const newGasPrice = customGasPrice || bnToHex(hexToBn(lastGasPrice).mul(1.1))
const newTxMeta = this.txStateManager.generateTxMeta({
txParams: {
from,
to: from,
nonce,
gas: '0x5208',
value: '0x0',
gasPrice: newGasPrice,
},
lastGasPrice,
loadingDefaults: false,
status: TRANSACTION_STATUS_APPROVED,
type: TRANSACTION_TYPE_CANCEL,
})
this.addTx(newTxMeta)
await this.approveTransaction(newTxMeta.id)
return newTxMeta
}
/** /**
updates the txMeta in the txStateManager updates the txMeta in the txStateManager
@param txMeta {Object} - the updated txMeta @param txMeta {Object} - the updated txMeta
@ -393,7 +438,7 @@ class TransactionController extends EventEmitter {
}) })
this.txStateManager.getFilteredTxList({ this.txStateManager.getFilteredTxList({
status: 'approved', status: TRANSACTION_STATUS_APPROVED,
}).forEach((txMeta) => { }).forEach((txMeta) => {
const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)

@ -407,6 +407,7 @@ module.exports = class MetamaskController extends EventEmitter {
updateTransaction: nodeify(txController.updateTransaction, txController), updateTransaction: nodeify(txController.updateTransaction, txController),
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
retryTransaction: nodeify(this.retryTransaction, this), retryTransaction: nodeify(this.retryTransaction, this),
createCancelTransaction: nodeify(this.createCancelTransaction, this),
getFilteredTxList: nodeify(txController.getFilteredTxList, txController), getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
isNonceTaken: nodeify(txController.isNonceTaken, txController), isNonceTaken: nodeify(txController.isNonceTaken, txController),
estimateGas: nodeify(this.estimateGas, this), estimateGas: nodeify(this.estimateGas, this),
@ -1109,6 +1110,19 @@ module.exports = class MetamaskController extends EventEmitter {
return state return state
} }
/**
* Allows a user to attempt to cancel a previously submitted transaction by creating a new
* transaction.
* @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
* @returns {object} MetaMask state
*/
async createCancelTransaction (originalTxId, customGasPrice, cb) {
await this.txController.createCancelTransaction(originalTxId, customGasPrice)
const state = await this.getState()
return state
}
estimateGas (estimateGasParams) { estimateGas (estimateGasParams) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => { return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => {

@ -315,6 +315,8 @@ var actions = {
CLEAR_PENDING_TOKENS: 'CLEAR_PENDING_TOKENS', CLEAR_PENDING_TOKENS: 'CLEAR_PENDING_TOKENS',
setPendingTokens, setPendingTokens,
clearPendingTokens, clearPendingTokens,
createCancelTransaction,
} }
module.exports = actions module.exports = actions
@ -1772,6 +1774,29 @@ function retryTransaction (txId) {
} }
} }
function createCancelTransaction (txId, customGasPrice) {
log.debug('background.cancelTransaction')
let newTxId
return dispatch => {
return new Promise((resolve, reject) => {
background.createCancelTransaction(txId, customGasPrice, (err, newState) => {
if (err) {
dispatch(actions.displayWarning(err.message))
reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
newTxId = id
resolve(newState)
})
})
.then(newState => dispatch(actions.updateMetamaskState(newState)))
.then(() => newTxId)
}
}
// //
// config // config
// //

Loading…
Cancel
Save