mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #1276 from MetaMask/ImproveGasEstimates
Improve UI gas calculation logic
This commit is contained in:
commit
0f1ea5861f
7
app/scripts/lib/hex-to-bn.js
Normal file
7
app/scripts/lib/hex-to-bn.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const BN = ethUtil.BN
|
||||||
|
|
||||||
|
module.exports = function hexToBn (hex) {
|
||||||
|
return new BN(ethUtil.stripHexPrefix(hex), 16)
|
||||||
|
}
|
||||||
|
|
@ -12,48 +12,49 @@ and used to do things like calculate gas of a tx.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = class txProviderUtils {
|
module.exports = class txProviderUtils {
|
||||||
|
|
||||||
constructor (provider) {
|
constructor (provider) {
|
||||||
this.provider = provider
|
this.provider = provider
|
||||||
this.query = new EthQuery(provider)
|
this.query = new EthQuery(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeGasUsage (txData, cb) {
|
analyzeGasUsage (txMeta, cb) {
|
||||||
var self = this
|
var self = this
|
||||||
this.query.getBlockByNumber('latest', true, (err, block) => {
|
this.query.getBlockByNumber('latest', true, (err, block) => {
|
||||||
if (err) return cb(err)
|
if (err) return cb(err)
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
self.estimateTxGas.bind(self, txData, block.gasLimit),
|
self.estimateTxGas.bind(self, txMeta, block.gasLimit),
|
||||||
self.setTxGas.bind(self, txData, block.gasLimit),
|
self.setTxGas.bind(self, txMeta, block.gasLimit),
|
||||||
], cb)
|
], cb)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
estimateTxGas (txData, blockGasLimitHex, cb) {
|
estimateTxGas (txMeta, blockGasLimitHex, cb) {
|
||||||
const txParams = txData.txParams
|
const txParams = txMeta.txParams
|
||||||
// check if gasLimit is already specified
|
// check if gasLimit is already specified
|
||||||
txData.gasLimitSpecified = Boolean(txParams.gas)
|
txMeta.gasLimitSpecified = Boolean(txParams.gas)
|
||||||
// if not, fallback to block gasLimit
|
// if not, fallback to block gasLimit
|
||||||
if (!txData.gasLimitSpecified) {
|
if (!txMeta.gasLimitSpecified) {
|
||||||
txParams.gas = blockGasLimitHex
|
txParams.gas = blockGasLimitHex
|
||||||
}
|
}
|
||||||
// run tx, see if it will OOG
|
// run tx, see if it will OOG
|
||||||
this.query.estimateGas(txParams, cb)
|
this.query.estimateGas(txParams, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
setTxGas (txData, blockGasLimitHex, estimatedGasHex, cb) {
|
setTxGas (txMeta, blockGasLimitHex, estimatedGasHex, cb) {
|
||||||
txData.estimatedGas = estimatedGasHex
|
txMeta.estimatedGas = estimatedGasHex
|
||||||
const txParams = txData.txParams
|
const txParams = txMeta.txParams
|
||||||
|
|
||||||
// if gasLimit was specified and doesnt OOG,
|
// if gasLimit was specified and doesnt OOG,
|
||||||
// use original specified amount
|
// use original specified amount
|
||||||
if (txData.gasLimitSpecified) {
|
if (txMeta.gasLimitSpecified) {
|
||||||
txData.estimatedGas = txParams.gas
|
txMeta.estimatedGas = txParams.gas
|
||||||
cb()
|
cb()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if gasLimit not originally specified,
|
// if gasLimit not originally specified,
|
||||||
// try adding an additional gas buffer to our estimation for safety
|
// try adding an additional gas buffer to our estimation for safety
|
||||||
const recommendedGasHex = this.addGasBuffer(txData.estimatedGas, blockGasLimitHex)
|
const recommendedGasHex = this.addGasBuffer(txMeta.estimatedGas, blockGasLimitHex)
|
||||||
txParams.gas = recommendedGasHex
|
txParams.gas = recommendedGasHex
|
||||||
cb()
|
cb()
|
||||||
return
|
return
|
||||||
@ -90,16 +91,13 @@ module.exports = class txProviderUtils {
|
|||||||
|
|
||||||
// builds ethTx from txParams object
|
// builds ethTx from txParams object
|
||||||
buildEthTxFromParams (txParams) {
|
buildEthTxFromParams (txParams) {
|
||||||
// apply gas multiplyer
|
|
||||||
let gasPrice = hexToBn(txParams.gasPrice)
|
|
||||||
// multiply and divide by 100 so as to add percision to integer mul
|
|
||||||
txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
|
|
||||||
// normalize values
|
// normalize values
|
||||||
txParams.to = normalize(txParams.to)
|
txParams.to = normalize(txParams.to)
|
||||||
txParams.from = normalize(txParams.from)
|
txParams.from = normalize(txParams.from)
|
||||||
txParams.value = normalize(txParams.value)
|
txParams.value = normalize(txParams.value)
|
||||||
txParams.data = normalize(txParams.data)
|
txParams.data = normalize(txParams.data)
|
||||||
txParams.gas = normalize(txParams.gas || txParams.gasLimit)
|
txParams.gas = normalize(txParams.gas || txParams.gasLimit)
|
||||||
|
txParams.gasPrice = normalize(txParams.gasPrice)
|
||||||
txParams.nonce = normalize(txParams.nonce)
|
txParams.nonce = normalize(txParams.nonce)
|
||||||
// build ethTx
|
// build ethTx
|
||||||
log.info(`Prepared tx for signing: ${JSON.stringify(txParams)}`)
|
log.info(`Prepared tx for signing: ${JSON.stringify(txParams)}`)
|
||||||
|
@ -4,7 +4,7 @@ const extend = require('xtend')
|
|||||||
const Semaphore = require('semaphore')
|
const Semaphore = require('semaphore')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const BN = require('ethereumjs-util').BN
|
const EthQuery = require('eth-query')
|
||||||
const TxProviderUtil = require('./lib/tx-utils')
|
const TxProviderUtil = require('./lib/tx-utils')
|
||||||
const createId = require('./lib/random-id')
|
const createId = require('./lib/random-id')
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
this.txHistoryLimit = opts.txHistoryLimit
|
this.txHistoryLimit = opts.txHistoryLimit
|
||||||
this.provider = opts.provider
|
this.provider = opts.provider
|
||||||
this.blockTracker = opts.blockTracker
|
this.blockTracker = opts.blockTracker
|
||||||
|
this.query = new EthQuery(this.provider)
|
||||||
this.txProviderUtils = new TxProviderUtil(this.provider)
|
this.txProviderUtils = new TxProviderUtil(this.provider)
|
||||||
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
|
||||||
this.signEthTx = opts.signTransaction
|
this.signEthTx = opts.signTransaction
|
||||||
@ -78,8 +79,9 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
fullTxList.splice(index, 1)
|
fullTxList.splice(index, 1)
|
||||||
}
|
}
|
||||||
fullTxList.push(txMeta)
|
fullTxList.push(txMeta)
|
||||||
|
|
||||||
this._saveTxList(fullTxList)
|
this._saveTxList(fullTxList)
|
||||||
|
this.emit('update')
|
||||||
|
|
||||||
this.once(`${txMeta.id}:signed`, function (txId) {
|
this.once(`${txMeta.id}:signed`, function (txId) {
|
||||||
this.removeAllListeners(`${txMeta.id}:rejected`)
|
this.removeAllListeners(`${txMeta.id}:rejected`)
|
||||||
})
|
})
|
||||||
@ -121,44 +123,38 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
async.waterfall([
|
async.waterfall([
|
||||||
// validate
|
// validate
|
||||||
(cb) => this.txProviderUtils.validateTxParams(txParams, cb),
|
(cb) => this.txProviderUtils.validateTxParams(txParams, cb),
|
||||||
// prepare txMeta
|
// construct txMeta
|
||||||
(cb) => {
|
(cb) => {
|
||||||
// create txMeta obj with parameters and meta data
|
|
||||||
let time = (new Date()).getTime()
|
|
||||||
let txId = createId()
|
|
||||||
txParams.metamaskId = txId
|
|
||||||
txParams.metamaskNetworkId = this.getNetwork()
|
|
||||||
txMeta = {
|
txMeta = {
|
||||||
id: txId,
|
id: createId(),
|
||||||
time: time,
|
time: (new Date()).getTime(),
|
||||||
status: 'unapproved',
|
status: 'unapproved',
|
||||||
metamaskNetworkId: this.getNetwork(),
|
metamaskNetworkId: this.getNetwork(),
|
||||||
txParams: txParams,
|
txParams: txParams,
|
||||||
}
|
}
|
||||||
// calculate metadata for tx
|
cb()
|
||||||
this.txProviderUtils.analyzeGasUsage(txMeta, cb)
|
|
||||||
},
|
},
|
||||||
|
// add default tx params
|
||||||
|
(cb) => this.addTxDefaults(txMeta, cb),
|
||||||
// save txMeta
|
// save txMeta
|
||||||
(cb) => {
|
(cb) => {
|
||||||
this.addTx(txMeta)
|
this.addTx(txMeta)
|
||||||
this.setMaxTxCostAndFee(txMeta)
|
|
||||||
cb(null, txMeta)
|
cb(null, txMeta)
|
||||||
},
|
},
|
||||||
], done)
|
], done)
|
||||||
}
|
}
|
||||||
|
|
||||||
setMaxTxCostAndFee (txMeta) {
|
addTxDefaults (txMeta, cb) {
|
||||||
var txParams = txMeta.txParams
|
const txParams = txMeta.txParams
|
||||||
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
|
// ensure value
|
||||||
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
|
txParams.value = txParams.value || '0x0'
|
||||||
var txFee = gasCost.mul(gasPrice)
|
this.query.gasPrice((err, gasPrice) => {
|
||||||
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
|
if (err) return cb(err)
|
||||||
var maxCost = txValue.add(txFee)
|
// set gasPrice
|
||||||
txMeta.txFee = txFee
|
txParams.gasPrice = gasPrice
|
||||||
txMeta.txValue = txValue
|
// set gasLimit
|
||||||
txMeta.maxCost = maxCost
|
this.txProviderUtils.analyzeGasUsage(txMeta, cb)
|
||||||
txMeta.gasPrice = gasPrice
|
})
|
||||||
this.updateTx(txMeta)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnapprovedTxList () {
|
getUnapprovedTxList () {
|
||||||
@ -336,7 +332,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
return this.setTxStatusFailed(txId, errReason)
|
return this.setTxStatusFailed(txId, errReason)
|
||||||
}
|
}
|
||||||
this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
|
this.query.getTransactionByHash(txHash, (err, txParams) => {
|
||||||
if (err || !txParams) {
|
if (err || !txParams) {
|
||||||
if (!txParams) return
|
if (!txParams) return
|
||||||
txMeta.err = {
|
txMeta.err = {
|
||||||
|
@ -11,7 +11,7 @@ module.exports = connect(mapStateToProps)(AccountsScreen)
|
|||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const pendingTxs = valuesFor(state.metamask.unapprovedTxs)
|
const pendingTxs = valuesFor(state.metamask.unapprovedTxs)
|
||||||
.filter(tx => tx.txParams.metamaskNetworkId === state.metamask.network)
|
.filter(txMeta => txMeta.metamaskNetworkId === state.metamask.network)
|
||||||
const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
||||||
const pending = pendingTxs.concat(pendingMsgs)
|
const pending = pendingTxs.concat(pendingMsgs)
|
||||||
|
|
||||||
|
@ -421,6 +421,7 @@ function updateAndApproveTx (txData) {
|
|||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
log.debug(`actions calling background.updateAndApproveTx`)
|
log.debug(`actions calling background.updateAndApproveTx`)
|
||||||
background.updateAndApproveTransaction(txData, (err) => {
|
background.updateAndApproveTransaction(txData, (err) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
dispatch(actions.txError(err))
|
dispatch(actions.txError(err))
|
||||||
return console.error(err.message)
|
return console.error(err.message)
|
||||||
|
@ -2,11 +2,11 @@ const Component = require('react').Component
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const extend = require('xtend')
|
|
||||||
const actions = require('../actions')
|
const actions = require('../actions')
|
||||||
|
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const BN = ethUtil.BN
|
const BN = ethUtil.BN
|
||||||
|
const hexToBn = require('../../../app/scripts/lib/hex-to-bn')
|
||||||
|
|
||||||
const MiniAccountPanel = require('./mini-account-panel')
|
const MiniAccountPanel = require('./mini-account-panel')
|
||||||
const EthBalance = require('./eth-balance')
|
const EthBalance = require('./eth-balance')
|
||||||
@ -29,42 +29,43 @@ function PendingTx () {
|
|||||||
Component.call(this)
|
Component.call(this)
|
||||||
this.state = {
|
this.state = {
|
||||||
valid: true,
|
valid: true,
|
||||||
gas: null,
|
|
||||||
gasPrice: null,
|
|
||||||
txData: null,
|
txData: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingTx.prototype.render = function () {
|
PendingTx.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const state = this.state
|
|
||||||
|
|
||||||
const txData = state.txData || props.txData
|
const txMeta = this.gatherTxMeta()
|
||||||
const txParams = txData.txParams || {}
|
const txParams = txMeta.txParams || {}
|
||||||
|
|
||||||
const address = txParams.from || props.selectedAddress
|
const address = txParams.from || props.selectedAddress
|
||||||
const identity = props.identities[address] || { address: address }
|
const identity = props.identities[address] || { address: address }
|
||||||
const account = props.accounts[address]
|
const account = props.accounts[address]
|
||||||
const balance = account ? account.balance : '0x0'
|
const balance = account ? account.balance : '0x0'
|
||||||
|
|
||||||
const gas = state.gas || txParams.gas
|
const gas = txParams.gas
|
||||||
const gasPrice = state.gasPrice || txData.gasPrice
|
const gasPrice = txParams.gasPrice
|
||||||
const gasBn = new BN(gas, 16)
|
|
||||||
const gasPriceBn = new BN(gasPrice, 16)
|
const gasBn = hexToBn(gas)
|
||||||
|
const gasPriceBn = hexToBn(gasPrice)
|
||||||
|
|
||||||
const txFeeBn = gasBn.mul(gasPriceBn)
|
const txFeeBn = gasBn.mul(gasPriceBn)
|
||||||
const valueBn = new BN(ethUtil.stripHexPrefix(txParams.value), 16)
|
const valueBn = hexToBn(txParams.value)
|
||||||
const maxCost = txFeeBn.add(valueBn)
|
const maxCost = txFeeBn.add(valueBn)
|
||||||
|
|
||||||
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
||||||
const imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
|
const imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
|
||||||
|
|
||||||
|
const balanceBn = hexToBn(balance)
|
||||||
|
const insufficientBalance = balanceBn.lt(maxCost)
|
||||||
|
|
||||||
this.inputs = []
|
this.inputs = []
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('div', {
|
h('div', {
|
||||||
key: txData.id,
|
key: txMeta.id,
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('form#pending-tx-form', {
|
h('form#pending-tx-form', {
|
||||||
@ -73,9 +74,8 @@ PendingTx.prototype.render = function () {
|
|||||||
const form = document.querySelector('form#pending-tx-form')
|
const form = document.querySelector('form#pending-tx-form')
|
||||||
const valid = form.checkValidity()
|
const valid = form.checkValidity()
|
||||||
this.setState({ valid })
|
this.setState({ valid })
|
||||||
|
|
||||||
if (valid && this.verifyGasParams()) {
|
if (valid && this.verifyGasParams()) {
|
||||||
props.sendTransaction(txData, event)
|
props.sendTransaction(txMeta, event)
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
|
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
|
||||||
}
|
}
|
||||||
@ -162,7 +162,8 @@ PendingTx.prototype.render = function () {
|
|||||||
h(HexInput, {
|
h(HexInput, {
|
||||||
name: 'Gas Limit',
|
name: 'Gas Limit',
|
||||||
value: gas,
|
value: gas,
|
||||||
min: MIN_GAS_LIMIT_BN.toString(10), // The hard lower limit for gas.
|
// The hard lower limit for gas.
|
||||||
|
min: MIN_GAS_LIMIT_BN.toString(10),
|
||||||
suffix: 'UNITS',
|
suffix: 'UNITS',
|
||||||
style: {
|
style: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -170,7 +171,9 @@ PendingTx.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
onChange: (newHex) => {
|
onChange: (newHex) => {
|
||||||
log.info(`Gas limit changed to ${newHex}`)
|
log.info(`Gas limit changed to ${newHex}`)
|
||||||
this.setState({ gas: newHex })
|
const txMeta = this.gatherTxMeta()
|
||||||
|
txMeta.txParams.gas = newHex
|
||||||
|
this.setState({ txData: txMeta })
|
||||||
},
|
},
|
||||||
ref: (hexInput) => { this.inputs.push(hexInput) },
|
ref: (hexInput) => { this.inputs.push(hexInput) },
|
||||||
}),
|
}),
|
||||||
@ -193,7 +196,9 @@ PendingTx.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
onChange: (newHex) => {
|
onChange: (newHex) => {
|
||||||
log.info(`Gas price changed to: ${newHex}`)
|
log.info(`Gas price changed to: ${newHex}`)
|
||||||
this.setState({ gasPrice: newHex })
|
const txMeta = this.gatherTxMeta()
|
||||||
|
txMeta.txParams.gasPrice = newHex
|
||||||
|
this.setState({ txData: txMeta })
|
||||||
},
|
},
|
||||||
ref: (hexInput) => { this.inputs.push(hexInput) },
|
ref: (hexInput) => { this.inputs.push(hexInput) },
|
||||||
}),
|
}),
|
||||||
@ -255,7 +260,7 @@ PendingTx.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
`),
|
`),
|
||||||
|
|
||||||
txData.simulationFails ?
|
txMeta.simulationFails ?
|
||||||
h('.error', {
|
h('.error', {
|
||||||
style: {
|
style: {
|
||||||
marginLeft: 50,
|
marginLeft: 50,
|
||||||
@ -264,7 +269,7 @@ PendingTx.prototype.render = function () {
|
|||||||
}, 'Transaction Error. Exception thrown in contract code.')
|
}, 'Transaction Error. Exception thrown in contract code.')
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
props.insufficientBalance ?
|
insufficientBalance ?
|
||||||
h('span.error', {
|
h('span.error', {
|
||||||
style: {
|
style: {
|
||||||
marginLeft: 50,
|
marginLeft: 50,
|
||||||
@ -283,7 +288,7 @@ PendingTx.prototype.render = function () {
|
|||||||
}, [
|
}, [
|
||||||
|
|
||||||
|
|
||||||
props.insufficientBalance ?
|
insufficientBalance ?
|
||||||
h('button', {
|
h('button', {
|
||||||
onClick: props.buyEth,
|
onClick: props.buyEth,
|
||||||
}, 'Buy Ether')
|
}, 'Buy Ether')
|
||||||
@ -301,7 +306,7 @@ PendingTx.prototype.render = function () {
|
|||||||
type: 'submit',
|
type: 'submit',
|
||||||
value: 'ACCEPT',
|
value: 'ACCEPT',
|
||||||
style: { marginLeft: '10px' },
|
style: { marginLeft: '10px' },
|
||||||
disabled: props.insufficientBalance || !this.state.valid,
|
disabled: insufficientBalance || !this.state.valid,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('button.cancel.btn-red', {
|
h('button.cancel.btn-red', {
|
||||||
@ -313,10 +318,6 @@ PendingTx.prototype.render = function () {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingTx.prototype.validChanged = function (newValid) {
|
|
||||||
this.setState({ valid: newValid })
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingTx.prototype.miniAccountPanelForRecipient = function () {
|
PendingTx.prototype.miniAccountPanelForRecipient = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const txData = props.txData
|
const txData = props.txData
|
||||||
@ -358,63 +359,8 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingTx.prototype.componentDidUpdate = function (prevProps, previousState) {
|
|
||||||
log.debug(`pending-tx componentDidUpdate`)
|
|
||||||
const state = this.state || {}
|
|
||||||
const prevState = previousState || {}
|
|
||||||
const { gas, gasPrice } = state
|
|
||||||
|
|
||||||
// Only if gas or gasPrice changed:
|
|
||||||
if (!prevState ||
|
|
||||||
(gas !== prevState.gas ||
|
|
||||||
gasPrice !== prevState.gasPrice)) {
|
|
||||||
log.debug(`recalculating gas since prev state change: ${JSON.stringify({ prevState, state })}`)
|
|
||||||
this.calculateGas()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingTx.prototype.calculateGas = function () {
|
|
||||||
const state = this.state
|
|
||||||
const props = this.props
|
|
||||||
const txData = props.txData
|
|
||||||
|
|
||||||
const txMeta = this.gatherParams()
|
|
||||||
log.debug(`pending-tx calculating gas for ${JSON.stringify(txMeta)}`)
|
|
||||||
|
|
||||||
const txParams = txMeta.txParams
|
|
||||||
const gasLimit = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
|
|
||||||
const gasPriceHex = state.gasPrice || txData.gasPrice
|
|
||||||
const gasPrice = new BN(ethUtil.stripHexPrefix(gasPriceHex), 16)
|
|
||||||
|
|
||||||
const valid = !gasPrice.lt(MIN_GAS_PRICE_BN) && !gasLimit.lt(MIN_GAS_LIMIT_BN)
|
|
||||||
this.validChanged(valid)
|
|
||||||
|
|
||||||
const txFee = gasLimit.mul(gasPrice)
|
|
||||||
const txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
|
|
||||||
const maxCost = txValue.add(txFee)
|
|
||||||
|
|
||||||
const txFeeHex = '0x' + txFee.toString('hex')
|
|
||||||
const maxCostHex = '0x' + maxCost.toString('hex')
|
|
||||||
|
|
||||||
txMeta.txFee = txFeeHex
|
|
||||||
txMeta.maxCost = maxCostHex
|
|
||||||
txMeta.txParams.gasPrice = gasPriceHex
|
|
||||||
|
|
||||||
const newState = {
|
|
||||||
txFee: '0x' + txFee.toString('hex'),
|
|
||||||
maxCost: '0x' + maxCost.toString('hex'),
|
|
||||||
}
|
|
||||||
log.info(`tx form updating local state with ${JSON.stringify(newState)}`)
|
|
||||||
this.setState(newState)
|
|
||||||
|
|
||||||
if (this.props.onTxChange) {
|
|
||||||
this.props.onTxChange(txMeta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingTx.prototype.resetGasFields = function () {
|
PendingTx.prototype.resetGasFields = function () {
|
||||||
log.debug(`pending-tx resetGasFields`)
|
log.debug(`pending-tx resetGasFields`)
|
||||||
const txData = this.props.txData
|
|
||||||
|
|
||||||
this.inputs.forEach((hexInput) => {
|
this.inputs.forEach((hexInput) => {
|
||||||
if (hexInput) {
|
if (hexInput) {
|
||||||
@ -423,36 +369,29 @@ PendingTx.prototype.resetGasFields = function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
gas: txData.txParams.gas,
|
txData: null,
|
||||||
gasPrice: txData.gasPrice,
|
|
||||||
valid: true,
|
valid: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// After a customizable state value has been updated,
|
// After a customizable state value has been updated,
|
||||||
PendingTx.prototype.gatherParams = function () {
|
PendingTx.prototype.gatherTxMeta = function () {
|
||||||
log.debug(`pending-tx gatherParams`)
|
log.debug(`pending-tx gatherTxMeta`)
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const state = this.state || {}
|
const state = this.state
|
||||||
const txData = state.txData || props.txData
|
const txData = state.txData || props.txData
|
||||||
const txParams = txData.txParams
|
|
||||||
const gas = state.gas || txParams.gas
|
log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||||
const gasPrice = state.gasPrice || txParams.gasPrice
|
return txData
|
||||||
const resultTx = extend(txParams, {
|
|
||||||
gas,
|
|
||||||
gasPrice,
|
|
||||||
})
|
|
||||||
const resultTxMeta = extend(txData, {
|
|
||||||
txParams: resultTx,
|
|
||||||
})
|
|
||||||
log.debug(`UI has computed tx params ${JSON.stringify(resultTx)}`)
|
|
||||||
return resultTxMeta
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingTx.prototype.verifyGasParams = function () {
|
PendingTx.prototype.verifyGasParams = function () {
|
||||||
// We call this in case the gas has not been modified at all
|
// We call this in case the gas has not been modified at all
|
||||||
if (!this.state) { return true }
|
if (!this.state) { return true }
|
||||||
return this._notZeroOrEmptyString(this.state.gas) && this._notZeroOrEmptyString(this.state.gasPrice)
|
return (
|
||||||
|
this._notZeroOrEmptyString(this.state.gas) &&
|
||||||
|
this._notZeroOrEmptyString(this.state.gasPrice)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingTx.prototype._notZeroOrEmptyString = function (obj) {
|
PendingTx.prototype._notZeroOrEmptyString = function (obj) {
|
||||||
|
@ -7,8 +7,6 @@ const actions = require('./actions')
|
|||||||
const NetworkIndicator = require('./components/network')
|
const NetworkIndicator = require('./components/network')
|
||||||
const txHelper = require('../lib/tx-helper')
|
const txHelper = require('../lib/tx-helper')
|
||||||
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
||||||
const ethUtil = require('ethereumjs-util')
|
|
||||||
const BN = ethUtil.BN
|
|
||||||
|
|
||||||
const PendingTx = require('./components/pending-tx')
|
const PendingTx = require('./components/pending-tx')
|
||||||
const PendingMsg = require('./components/pending-msg')
|
const PendingMsg = require('./components/pending-msg')
|
||||||
@ -104,9 +102,6 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
selectedAddress: props.selectedAddress,
|
selectedAddress: props.selectedAddress,
|
||||||
accounts: props.accounts,
|
accounts: props.accounts,
|
||||||
identities: props.identities,
|
identities: props.identities,
|
||||||
insufficientBalance: this.checkBalanceAgainstTx(txData),
|
|
||||||
// State actions
|
|
||||||
onTxChange: this.onTxChange.bind(this),
|
|
||||||
// Actions
|
// Actions
|
||||||
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
|
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
|
||||||
sendTransaction: this.sendTransaction.bind(this, txData),
|
sendTransaction: this.sendTransaction.bind(this, txData),
|
||||||
@ -145,37 +140,14 @@ function currentTxView (opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
|
|
||||||
if (!txData.txParams) return false
|
|
||||||
var props = this.props
|
|
||||||
var address = txData.txParams.from || props.selectedAddress
|
|
||||||
var account = props.accounts[address]
|
|
||||||
var balance = account ? account.balance : '0x0'
|
|
||||||
var maxCost = new BN(txData.maxCost, 16)
|
|
||||||
|
|
||||||
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
|
|
||||||
return maxCost.gt(balanceBn)
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfirmTxScreen.prototype.buyEth = function (address, event) {
|
ConfirmTxScreen.prototype.buyEth = function (address, event) {
|
||||||
this.stopPropagation(event)
|
this.stopPropagation(event)
|
||||||
this.props.dispatch(actions.buyEthView(address))
|
this.props.dispatch(actions.buyEthView(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows the detail view to update the gas calculations,
|
|
||||||
// for manual gas controls.
|
|
||||||
ConfirmTxScreen.prototype.onTxChange = function (txData) {
|
|
||||||
log.debug(`conf-tx onTxChange triggered with ${JSON.stringify(txData)}`)
|
|
||||||
this.setState({ txData })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must default to any local state txData,
|
|
||||||
// to allow manual override of gas calculations.
|
|
||||||
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
|
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
|
||||||
this.stopPropagation(event)
|
this.stopPropagation(event)
|
||||||
const state = this.state || {}
|
this.props.dispatch(actions.updateAndApproveTx(txData))
|
||||||
const txMeta = state.txData
|
|
||||||
this.props.dispatch(actions.updateAndApproveTx(txMeta || txData))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
|
ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
|
||||||
|
@ -4,7 +4,7 @@ module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, network)
|
|||||||
log.debug('tx-helper called with params:')
|
log.debug('tx-helper called with params:')
|
||||||
log.debug({ unapprovedTxs, unapprovedMsgs, personalMsgs, network })
|
log.debug({ unapprovedTxs, unapprovedMsgs, personalMsgs, network })
|
||||||
|
|
||||||
const txValues = network ? valuesFor(unapprovedTxs).filter(tx => tx.txParams.metamaskNetworkId === network) : valuesFor(unapprovedTxs)
|
const txValues = network ? valuesFor(unapprovedTxs).filter(txMeta => txMeta.metamaskNetworkId === network) : valuesFor(unapprovedTxs)
|
||||||
log.debug(`tx helper found ${txValues.length} unapproved txs`)
|
log.debug(`tx helper found ${txValues.length} unapproved txs`)
|
||||||
const msgValues = valuesFor(unapprovedMsgs)
|
const msgValues = valuesFor(unapprovedMsgs)
|
||||||
log.debug(`tx helper found ${msgValues.length} unsigned messages`)
|
log.debug(`tx helper found ${msgValues.length} unsigned messages`)
|
||||||
@ -13,5 +13,5 @@ module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, network)
|
|||||||
log.debug(`tx helper found ${personalValues.length} unsigned personal messages`)
|
log.debug(`tx helper found ${personalValues.length} unsigned personal messages`)
|
||||||
allValues = allValues.concat(personalValues)
|
allValues = allValues.concat(personalValues)
|
||||||
|
|
||||||
return allValues.sort(tx => tx.time)
|
return allValues.sort(txMeta => txMeta.time)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user