2020-05-07 17:19:09 -04:00
const { redisClient } = require ( './redis' )
const config = require ( '../config' )
2020-05-08 13:29:31 -04:00
const { toBN , toHex , toWei , BN , fromWei } = require ( 'web3-utils' )
2020-05-07 17:19:09 -04:00
class Sender {
2020-05-08 13:29:31 -04:00
constructor ( web3 ) {
2020-05-07 17:19:09 -04:00
this . web3 = web3
2020-05-08 13:29:31 -04:00
this . watherInterval = config . watherInterval
this . pendingTxTimeout = config . pendingTxTimeout
this . gasBumpPercentage = config . gasBumpPercentage
this . watcher ( )
2020-05-07 17:19:09 -04:00
}
2020-05-08 13:29:31 -04:00
async watcher ( ) {
try {
const networkNonce = await this . web3 . eth . getTransactionCount ( this . web3 . eth . defaultAccount )
let tx = await redisClient . get ( 'tx:' + networkNonce )
if ( tx ) {
tx = JSON . parse ( tx )
if ( Date . now ( ) - tx . date > this . pendingTxTimeout ) {
const newGasPrice = toBN ( tx . gasPrice ) . mul ( toBN ( this . gasBumpPercentage ) ) . div ( toBN ( 100 ) )
const maxGasPrice = toBN ( toWei ( config . maxGasPrice ) )
tx . gasPrice = toHex ( BN . min ( newGasPrice , maxGasPrice ) )
tx . date = Date . now ( )
await redisClient . set ( 'tx:' + tx . nonce , JSON . stringify ( tx ) )
console . log ( 'resubmitting with gas price' , fromWei ( tx . gasPrice . toString ( ) ) )
this . sendTx ( tx , null , 9999 )
}
}
} catch ( e ) {
console . error ( 'watcher error:' , e )
} finally {
setTimeout ( ( ) => this . watcher ( ) , this . watherInterval )
2020-05-07 17:19:09 -04:00
}
}
2020-05-08 13:29:31 -04:00
async sendTx ( tx , done , retryAttempt = 1 ) {
2020-05-07 17:19:09 -04:00
let signedTx = await this . web3 . eth . accounts . signTransaction ( tx , config . privateKey )
let result = this . web3 . eth . sendSignedTransaction ( signedTx . rawTransaction )
2020-05-08 13:29:31 -04:00
result . once ( 'transactionHash' , function ( txHash ) {
console . log ( ` A new successfully sent tx ${ txHash } ` )
if ( done ) {
done ( null , {
status : 200 ,
msg : { txHash }
} )
}
2020-05-07 17:19:09 -04:00
} ) . on ( 'error' , async function ( e ) {
2020-05-08 13:29:31 -04:00
console . log ( ` Error for tx with nonce ${ tx . nonce } \n ${ e . message } ` )
if ( e . message === 'Returned error: Transaction gas price supplied is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.'
2020-05-07 17:19:09 -04:00
|| e . message === 'Returned error: Transaction nonce is too low. Try incrementing the nonce.'
|| e . message === 'Returned error: nonce too low'
|| e . message === 'Returned error: replacement transaction underpriced' ) {
console . log ( 'nonce too low, retrying' )
if ( retryAttempt <= 10 ) {
retryAttempt ++
const newNonce = tx . nonce + 1
tx . nonce = newNonce
await redisClient . set ( 'nonce' , newNonce )
2020-05-08 13:29:31 -04:00
await redisClient . set ( 'tx:' + newNonce , JSON . stringify ( tx ) )
this . sendTx ( tx , done , retryAttempt )
2020-05-07 17:19:09 -04:00
return
}
}
2020-05-08 13:29:31 -04:00
if ( done ) {
done ( null , {
status : 400 ,
msg : { error : 'Internal Relayer Error. Please use a different relayer service' }
} )
}
2020-05-07 17:19:09 -04:00
} )
}
}
2020-05-08 13:29:31 -04:00
module . exports = Sender