aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/lib/tx-utils.js
blob: a976173f5931f9306c854f5df3304be765eec65b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const async = require('async')
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN

/*
tx-utils are utility methods for Transaction manager
its passed a provider and that is passed to ethquery
and used to do things like calculate gas of a tx.
*/

module.exports = class txProviderUtils {
  constructor (provider) {
    this.provider = provider
    this.query = new EthQuery(provider)
  }
  analyzeGasUsage (txData, cb) {
    var self = this
    this.query.getBlockByNumber('latest', true, (err, block) => {
      if (err) return cb(err)
      async.waterfall([
        self.estimateTxGas.bind(self, txData, block.gasLimit),
        self.checkForTxGasError.bind(self, txData),
        self.setTxGas.bind(self, txData, block.gasLimit),
      ], cb)
    })
  }

  estimateTxGas (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
    this.query.estimateGas(txParams, cb)
  }

  checkForTxGasError (txData, estimatedGasHex, cb) {
    txData.estimatedGas = estimatedGasHex
    // all gas used - must be an error
    if (estimatedGasHex === txData.txParams.gas) {
      txData.simulationFails = true
    }
    cb()
  }

  setTxGas (txData, blockGasLimitHex, cb) {
    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 = new BN(this.addGasBuffer(estimatedGasBn), 16)
    // 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
  }

  addGasBuffer (gas) {
    const gasBuffer = new BN('100000', 10)
    const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
    const correct = bnGas.add(gasBuffer)
    return ethUtil.addHexPrefix(correct.toString(16))
  }
}