aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/controllers/transactions/tx-gas-utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts/controllers/transactions/tx-gas-utils.js')
-rw-r--r--app/scripts/controllers/transactions/tx-gas-utils.js129
1 files changed, 129 insertions, 0 deletions
diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js
new file mode 100644
index 000000000..36b5cdbc9
--- /dev/null
+++ b/app/scripts/controllers/transactions/tx-gas-utils.js
@@ -0,0 +1,129 @@
+const EthQuery = require('ethjs-query')
+const {
+ hexToBn,
+ BnMultiplyByFraction,
+ bnToHex,
+} = require('../../lib/util')
+const { addHexPrefix } = require('ethereumjs-util')
+const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
+
+/**
+tx-gas-utils are gas utility methods for Transaction manager
+its passed ethquery
+and used to do things like calculate gas of a tx.
+@param {Object} provider - A network provider.
+*/
+
+class TxGasUtil {
+
+ constructor (provider) {
+ this.query = new EthQuery(provider)
+ }
+
+ /**
+ @param txMeta {Object} - the txMeta object
+ @returns {object} the txMeta object with the gas written to the txParams
+ */
+ async analyzeGasUsage (txMeta) {
+ const block = await this.query.getBlockByNumber('latest', true)
+ let estimatedGasHex
+ try {
+ estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
+ } catch (err) {
+ const simulationFailed = (
+ err.message.includes('Transaction execution error.') ||
+ err.message.includes('gas required exceeds allowance or always failing transaction')
+ )
+ if (simulationFailed) {
+ txMeta.simulationFails = true
+ return txMeta
+ }
+ }
+ this.setTxGas(txMeta, block.gasLimit, estimatedGasHex)
+ return txMeta
+ }
+
+ /**
+ Estimates the tx's gas usage
+ @param txMeta {Object} - the txMeta object
+ @param blockGasLimitHex {string} - hex string of the block's gas limit
+ @returns {string} the estimated gas limit as a hex string
+ */
+ async estimateTxGas (txMeta, blockGasLimitHex) {
+ const txParams = txMeta.txParams
+
+ // check if gasLimit is already specified
+ txMeta.gasLimitSpecified = Boolean(txParams.gas)
+
+ // if it is, use that value
+ if (txMeta.gasLimitSpecified) {
+ return txParams.gas
+ }
+
+ // if recipient has no code, gas is 21k max:
+ const recipient = txParams.to
+ const hasRecipient = Boolean(recipient)
+ let code
+ if (recipient) code = await this.query.getCode(recipient)
+
+ if (hasRecipient && (!code || code === '0x')) {
+ txParams.gas = SIMPLE_GAS_COST
+ txMeta.simpleSend = true // Prevents buffer addition
+ return SIMPLE_GAS_COST
+ }
+
+ // if not, fall back to block gasLimit
+ const blockGasLimitBN = hexToBn(blockGasLimitHex)
+ const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20)
+ txParams.gas = bnToHex(saferGasLimitBN)
+
+ // run tx
+ return await this.query.estimateGas(txParams)
+ }
+
+ /**
+ Writes the gas on the txParams in the txMeta
+ @param txMeta {Object} - the txMeta object to write to
+ @param blockGasLimitHex {string} - the block gas limit hex
+ @param estimatedGasHex {string} - the estimated gas hex
+ */
+ setTxGas (txMeta, blockGasLimitHex, estimatedGasHex) {
+ txMeta.estimatedGas = addHexPrefix(estimatedGasHex)
+ const txParams = txMeta.txParams
+
+ // if gasLimit was specified and doesnt OOG,
+ // use original specified amount
+ if (txMeta.gasLimitSpecified || txMeta.simpleSend) {
+ txMeta.estimatedGas = txParams.gas
+ return
+ }
+ // if gasLimit not originally specified,
+ // try adding an additional gas buffer to our estimation for safety
+ const recommendedGasHex = this.addGasBuffer(txMeta.estimatedGas, blockGasLimitHex)
+ txParams.gas = recommendedGasHex
+ return
+ }
+
+ /**
+ Adds a gas buffer with out exceeding the block gas limit
+
+ @param initialGasLimitHex {string} - the initial gas limit to add the buffer too
+ @param blockGasLimitHex {string} - the block gas limit
+ @returns {string} the buffered gas limit as a hex string
+ */
+ addGasBuffer (initialGasLimitHex, blockGasLimitHex) {
+ const initialGasLimitBn = hexToBn(initialGasLimitHex)
+ const blockGasLimitBn = hexToBn(blockGasLimitHex)
+ const upperGasLimitBn = blockGasLimitBn.muln(0.9)
+ const bufferedGasLimitBn = initialGasLimitBn.muln(1.5)
+
+ // if initialGasLimit is above blockGasLimit, dont modify it
+ if (initialGasLimitBn.gt(upperGasLimitBn)) return bnToHex(initialGasLimitBn)
+ // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
+ if (bufferedGasLimitBn.lt(upperGasLimitBn)) return bnToHex(bufferedGasLimitBn)
+ // otherwise use blockGasLimit
+ return bnToHex(upperGasLimitBn)
+ }
+}
+
+module.exports = TxGasUtil \ No newline at end of file