2020-05-08 00:19:09 +03:00
const { redisClient } = require ( './redis' )
const config = require ( '../config' )
2020-05-08 20:29:31 +03:00
const { toBN , toHex , toWei , BN , fromWei } = require ( 'web3-utils' )
2020-05-08 00:19:09 +03:00
class Sender {
2020-05-08 20:29:31 +03:00
constructor ( web3 ) {
2020-05-08 00:19:09 +03:00
this . web3 = web3
2020-05-08 20:29:31 +03:00
this . watherInterval = config . watherInterval
this . pendingTxTimeout = config . pendingTxTimeout
2020-05-08 22:03:33 +03:00
this . gasBumpPercentage = 100 + Number ( config . gasBumpPercentage )
2020-05-08 20:29:31 +03:00
this . watcher ( )
2020-05-08 00:19:09 +03:00
}
2020-05-08 20:29:31 +03: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 ) )
2020-08-04 10:41:23 +03:00
const maxGasPrice = toBN ( toWei ( config . maxGasPrice . toString ( ) , 'Gwei' ) )
2020-05-08 20:29:31 +03:00
tx . gasPrice = toHex ( BN . min ( newGasPrice , maxGasPrice ) )
tx . date = Date . now ( )
2020-07-16 16:33:35 +03:00
await redisClient . set ( 'tx:' + tx . nonce , JSON . stringify ( tx ) )
2020-05-08 22:03:33 +03:00
console . log ( 'resubmitting with gas price' , fromWei ( tx . gasPrice . toString ( ) , 'gwei' ) , ' gwei' )
2020-05-08 20:29:31 +03:00
this . sendTx ( tx , null , 9999 )
}
}
2020-07-16 16:33:35 +03:00
} catch ( e ) {
2020-05-08 20:29:31 +03:00
console . error ( 'watcher error:' , e )
} finally {
setTimeout ( ( ) => this . watcher ( ) , this . watherInterval )
2020-05-08 00:19:09 +03:00
}
}
2020-05-08 20:29:31 +03:00
async sendTx ( tx , done , retryAttempt = 1 ) {
2020-05-08 00:19:09 +03:00
let signedTx = await this . web3 . eth . accounts . signTransaction ( tx , config . privateKey )
let result = this . web3 . eth . sendSignedTransaction ( signedTx . rawTransaction )
2020-05-08 20:29:31 +03:00
2020-08-04 10:39:56 +03:00
result
. once ( 'transactionHash' , ( txHash ) => {
console . log ( ` A new successfully sent tx ${ txHash } ` )
if ( done ) {
done ( null , {
status : 200 ,
msg : { txHash }
} )
2020-05-08 00:19:09 +03:00
}
2020-08-04 10:39:56 +03:00
} )
. on ( 'error' , async ( e ) => {
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.' ||
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 )
await redisClient . set ( 'tx:' + newNonce , JSON . stringify ( tx ) )
this . sendTx ( tx , done , retryAttempt )
return
}
}
if ( done ) {
done ( null , {
status : 400 ,
msg : { error : 'Internal Relayer Error. Please use a different relayer service' }
} )
}
} )
2020-05-08 00:19:09 +03:00
}
}
2020-05-08 20:29:31 +03:00
module . exports = Sender