From ef8a07c2ce2b1c5fc4ef18f48592b2e7da178c44 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 8 May 2019 16:48:33 -0230 Subject: [PATCH] Adds a transactionCategory to txMeta for use in UI (#6567) * Adds a transactionCategory to txMeta for use in UI * Update transaction controller and tx-gas-util documentation on new code param in multiple functions. --- app/scripts/controllers/transactions/index.js | 55 ++++++++++++++++++- .../controllers/transactions/tx-gas-utils.js | 11 ++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 2ce736beb..dc6a043e4 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -3,6 +3,17 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const Transaction = require('ethereumjs-tx') const EthQuery = require('ethjs-query') +const abi = require('human-standard-token-abi') +const abiDecoder = require('abi-decoder') +abiDecoder.addABI(abi) +const { + TOKEN_METHOD_APPROVE, + TOKEN_METHOD_TRANSFER, + TOKEN_METHOD_TRANSFER_FROM, + SEND_ETHER_ACTION_KEY, + DEPLOY_CONTRACT_ACTION_KEY, + CONTRACT_INTERACTION_KEY, +} = require('../../../../ui/app/helpers/constants/transactions.js') const TransactionStateManager = require('./tx-state-manager') const TxGasUtil = require('./tx-gas-utils') const PendingTransactionTracker = require('./pending-tx-tracker') @@ -180,9 +191,11 @@ class TransactionController extends EventEmitter { } txUtils.validateTxParams(normalizedTxParams) // construct txMeta + const { transactionCategory, code } = await this._determineTransactionCategory(txParams) let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams, type: TRANSACTION_TYPE_STANDARD, + transactionCategory, }) this.addTx(txMeta) this.emit('newUnapprovedTx', txMeta) @@ -191,7 +204,7 @@ class TransactionController extends EventEmitter { // check whether recipient account is blacklisted recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to) // add default tx params - txMeta = await this.addTxGasDefaults(txMeta) + txMeta = await this.addTxGasDefaults(txMeta, code) } catch (error) { log.warn(error) txMeta.loadingDefaults = false @@ -211,7 +224,7 @@ class TransactionController extends EventEmitter { @param txMeta {Object} - the txMeta object @returns {Promise} resolves with txMeta */ - async addTxGasDefaults (txMeta) { + async addTxGasDefaults (txMeta, code) { const txParams = txMeta.txParams // ensure value txParams.value = txParams.value ? ethUtil.addHexPrefix(txParams.value) : '0x0' @@ -222,7 +235,7 @@ class TransactionController extends EventEmitter { } txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) // set gasLimit - return await this.txGasUtil.analyzeGasUsage(txMeta) + return await this.txGasUtil.analyzeGasUsage(txMeta, code) } /** @@ -555,6 +568,42 @@ class TransactionController extends EventEmitter { }) } + /** + Returns a "type" for a transaction out of the following list: simpleSend, tokenTransfer, tokenApprove, + contractDeployment, contractMethodCall + */ + async _determineTransactionCategory (txParams) { + const { data, to } = txParams + const { name } = data && abiDecoder.decodeMethod(data) || {} + const tokenMethodName = [ + TOKEN_METHOD_APPROVE, + TOKEN_METHOD_TRANSFER, + TOKEN_METHOD_TRANSFER_FROM, + ].find(tokenMethodName => tokenMethodName === name && name.toLowerCase()) + + let result + let code + if (!txParams.data) { + result = SEND_ETHER_ACTION_KEY + } else if (tokenMethodName) { + result = tokenMethodName + } else if (!to) { + result = DEPLOY_CONTRACT_ACTION_KEY + } else { + try { + code = await this.query.getCode(to) + } catch (e) { + log.warn(e) + } + // For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0' + const codeIsEmpty = !code || code === '0x' || code === '0x0' + + result = codeIsEmpty ? SEND_ETHER_ACTION_KEY : CONTRACT_INTERACTION_KEY + } + + return { transactionCategory: result, code } + } + /** Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions in the list have the same nonce diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js index 765551167..2dc461f48 100644 --- a/app/scripts/controllers/transactions/tx-gas-utils.js +++ b/app/scripts/controllers/transactions/tx-gas-utils.js @@ -4,6 +4,7 @@ const { BnMultiplyByFraction, bnToHex, } = require('../../lib/util') +const log = require('loglevel') const { addHexPrefix } = require('ethereumjs-util') const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. @@ -24,14 +25,16 @@ class TxGasUtil { /** @param txMeta {Object} - the txMeta object + @param code {string} - the code at the txs address, as returned by this.query.getCode(to) @returns {object} the txMeta object with the gas written to the txParams */ - async analyzeGasUsage (txMeta) { + async analyzeGasUsage (txMeta, code) { const block = await this.query.getBlockByNumber('latest', false) let estimatedGasHex try { - estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit) + estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit, code) } catch (err) { + log.warn(err) txMeta.simulationFails = { reason: err.message, errorKey: err.errorKey, @@ -52,9 +55,10 @@ class TxGasUtil { Estimates the tx's gas usage @param txMeta {Object} - the txMeta object @param blockGasLimitHex {string} - hex string of the block's gas limit + @param code {string} - the code at the txs address, as returned by this.query.getCode(to) @returns {string} the estimated gas limit as a hex string */ - async estimateTxGas (txMeta, blockGasLimitHex) { + async estimateTxGas (txMeta, blockGasLimitHex, code) { const txParams = txMeta.txParams // check if gasLimit is already specified @@ -70,7 +74,6 @@ class TxGasUtil { // see if we can set the gas based on the recipient if (hasRecipient) { - const code = await this.query.getCode(recipient) // For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0' const codeIsEmpty = !code || code === '0x' || code === '0x0'