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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
const async = require('async')
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const Transaction = require('ethereumjs-tx')
const normalize = require('eth-sig-util').normalize
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.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)
}
setTxGas (txData, blockGasLimitHex, estimatedGasHex, cb) {
txData.estimatedGas = estimatedGasHex
const txParams = txData.txParams
// 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))
}
fillInTxParams (txParams, cb) {
let fromAddress = txParams.from
let reqs = {}
if (isUndef(txParams.gas)) reqs.gas = (cb) => this.query.estimateGas(txParams, cb)
if (isUndef(txParams.gasPrice)) reqs.gasPrice = (cb) => this.query.gasPrice(cb)
if (isUndef(txParams.nonce)) reqs.nonce = (cb) => this.query.getTransactionCount(fromAddress, 'pending', cb)
async.parallel(reqs, function(err, result) {
if (err) return cb(err)
// write results to txParams obj
Object.assign(txParams, result)
cb()
})
}
// builds ethTx from txParams object
buildEthTxFromParams (txParams, gasMultiplier = 1) {
// apply gas multiplyer
let gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
// multiply and divide by 100 so as to add percision to integer mul
gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
// normalize values
txParams.to = normalize(txParams.to)
txParams.from = normalize(txParams.from)
txParams.value = normalize(txParams.value)
txParams.data = normalize(txParams.data)
txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas)
txParams.nonce = normalize(txParams.nonce)
// build ethTx
const ethTx = new Transaction(txParams)
return ethTx
}
publishTransaction (rawTx, cb) {
this.query.sendRawTransaction(rawTx, cb)
}
validateTxParams (txParams, cb) {
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`))
} else {
cb()
}
}
}
// util
function isUndef(value) {
return value === undefined
}
|