diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 34d81ace3..7020c188a 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -1,4 +1,5 @@ const async = require('async') +const bind = require('ap').partial const ethUtil = require('ethereumjs-util') const ethBinToOps = require('eth-bin-to-ops') const EthQuery = require('eth-query') @@ -367,7 +368,7 @@ module.exports = class KeyringController extends EventEmitter { // calculate metadata for tx async.parallel([ analyzeForDelegateCall, - estimateGas, + analyzeGasUsage, ], didComplete) // perform static analyis on the target contract code @@ -390,14 +391,69 @@ module.exports = class KeyringController extends EventEmitter { } } - function estimateGas (cb) { - query.estimateGas(txParams, function (err, result) { + function analyzeGasUsage (cb) { + query.getBlockByNumber('latest', true, function (err, block) { if (err) return cb(err) - txData.estimatedGas = self.addGasBuffer(result) - cb() + async.waterfall([ + bind(estimateGas, txData, block.gasLimit), + bind(checkForGasError, txData), + bind(setTxGas, txData, block.gasLimit), + ], cb) }) } + function estimateGas(txData, blockGasLimitHex, cb) { + const txParams = txData.txParams + // check if gasLimit is already specified + txData.gasLimitSpecified = Boolean(txParams.gas) + // if not, fallback to block gasLimit + if (!txData.gasLimitSpecified) { + txParams.gas = blockGasLimitHex + } + // run tx, see if it will OOG + query.estimateGas(txParams, cb) + } + + function checkForGasError(txData, estimatedGasHex) { + txData.estimatedGas = estimatedGasHex + // all gas used - must be an error + if (estimatedGasHex === txData.txParams.gas) { + txData.simulationFails = true + } + cb() + } + + function setTxGas(txData, blockGasLimitHex) { + const txParams = txData.txParams + // if OOG, nothing more to do + if (txData.simulationFails) { + cb() + return + } + // if gasLimit was specified and doesnt OOG, + // use original specified amount + if (txData.gasLimitSpecified) { + txData.estimatedGas = txParams.gas + cb() + return + } + // if gasLimit not originally specified, + // try adding an additional gas buffer to our estimation for safety + const estimatedGasBn = new BN(ethUtil.stripHexPrefix(txData.estimatedGas), 16) + const blockGasLimitBn = new BN(ethUtil.stripHexPrefix(blockGasLimitHex), 16) + const estimationWithBuffer = self.addGasBuffer(estimatedGasBn) + // added gas buffer is too high + if (estimationWithBuffer.gt(blockGasLimitBn)) { + txParams.gas = txData.estimatedGas + // added gas buffer is safe + } else { + const gasWithBufferHex = ethUtil.intToHex(estimationWithBuffer) + txParams.gas = gasWithBufferHex + } + cb() + return + } + function didComplete (err) { if (err) return cb(err) configManager.addTx(txData) diff --git a/package.json b/package.json index cbe1a7b55..c6b389a6c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ ] }, "dependencies": { + "ap": "^0.2.0", "async": "^1.5.2", "bip39": "^2.2.0", "browserify-derequire": "^0.9.4",