Fix eip1559 transaction sending problems (#632)
This commit is contained in:
parent
8d732adba1
commit
8ec11d0476
@ -9,6 +9,8 @@ const { sendTx } = require('./tx/sendTx')
|
||||
const { getNonce, getChainId } = require('./tx/web3')
|
||||
const {
|
||||
addExtraGas,
|
||||
applyMinGasFeeBump,
|
||||
chooseGasPriceOptions,
|
||||
checkHTTPS,
|
||||
syncForEach,
|
||||
waitForFunds,
|
||||
@ -19,7 +21,7 @@ const {
|
||||
isInsufficientBalanceError,
|
||||
isNonceError
|
||||
} = require('./utils/utils')
|
||||
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
|
||||
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT, MIN_GAS_PRICE_BUMP_FACTOR } = require('./utils/constants')
|
||||
|
||||
const { ORACLE_TX_REDUNDANCY } = process.env
|
||||
|
||||
@ -146,6 +148,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
}
|
||||
|
||||
try {
|
||||
const newGasPriceOptions = chooseGasPriceOptions(gasPriceOptions, job.gasPriceOptions)
|
||||
if (isResend) {
|
||||
const tx = await web3Fallback.eth.getTransaction(job.txHash)
|
||||
|
||||
@ -159,7 +162,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
}
|
||||
|
||||
const oldGasPrice = JSON.stringify(job.gasPriceOptions)
|
||||
const newGasPrice = JSON.stringify(gasPriceOptions)
|
||||
const newGasPrice = JSON.stringify(newGasPriceOptions)
|
||||
logger.info(`Transaction ${job.txHash} was not mined, updating gasPrice: ${oldGasPrice} -> ${newGasPrice}`)
|
||||
}
|
||||
logger.info(`Sending transaction with nonce ${nonce}`)
|
||||
@ -172,12 +175,12 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
to: job.to,
|
||||
chainId,
|
||||
web3: web3Redundant,
|
||||
gasPriceOptions
|
||||
gasPriceOptions: newGasPriceOptions
|
||||
})
|
||||
const resendJob = {
|
||||
...job,
|
||||
txHash,
|
||||
gasPriceOptions,
|
||||
...job
|
||||
gasPriceOptions: newGasPriceOptions
|
||||
}
|
||||
resendJobs.push(resendJob)
|
||||
|
||||
@ -196,7 +199,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
if (isGasPriceError(e)) {
|
||||
logger.info('Replacement transaction underpriced, forcing gas price update')
|
||||
GasPrice.start(config.id, web3)
|
||||
failedTx.push(job)
|
||||
failedTx.push(applyMinGasFeeBump(job, MIN_GAS_PRICE_BUMP_FACTOR))
|
||||
} else if (isResend || isSameTransactionError(e)) {
|
||||
resendJobs.push(job)
|
||||
} else {
|
||||
|
@ -24,6 +24,7 @@ module.exports = {
|
||||
MIN: 1,
|
||||
MAX: 1000
|
||||
},
|
||||
MIN_GAS_PRICE_BUMP_FACTOR: 0.1,
|
||||
DEFAULT_TRANSACTION_RESEND_INTERVAL: 20 * 60 * 1000,
|
||||
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
|
||||
BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT: 10,
|
||||
|
@ -2,6 +2,9 @@ const fs = require('fs')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const Web3 = require('web3')
|
||||
const { GAS_PRICE_BOUNDARIES } = require('./constants')
|
||||
|
||||
const { toBN, toWei } = Web3.utils
|
||||
|
||||
const retrySequence = [1, 2, 3, 5, 8, 13, 21, 34, 55, 60]
|
||||
|
||||
@ -34,8 +37,8 @@ const promiseRetryForever = f => promiseRetry(f, { forever: true, factor: 1 })
|
||||
async function waitForFunds(web3, address, minimumBalance, cb, logger) {
|
||||
promiseRetryForever(async retry => {
|
||||
logger.debug('Getting balance of validator account')
|
||||
const newBalance = web3.utils.toBN(await web3.eth.getBalance(address))
|
||||
if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) {
|
||||
const newBalance = toBN(await web3.eth.getBalance(address))
|
||||
if (newBalance.gte(toBN(minimumBalance.toString(10)))) {
|
||||
logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance')
|
||||
cb(newBalance)
|
||||
} else {
|
||||
@ -64,6 +67,48 @@ function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
|
||||
return BigNumber.min(maxGasLimit, gasWithExtra)
|
||||
}
|
||||
|
||||
function applyMinGasFeeBump(job, bumpFactor = 0.1) {
|
||||
if (!job.gasPriceOptions) {
|
||||
return job
|
||||
}
|
||||
const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = job.gasPriceOptions
|
||||
const maxGasPrice = toWei(GAS_PRICE_BOUNDARIES.MAX.toString(), 'gwei')
|
||||
if (gasPrice) {
|
||||
return {
|
||||
...job,
|
||||
gasPriceOptions: {
|
||||
gasPrice: addExtraGas(gasPrice, bumpFactor, maxGasPrice).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxFeePerGas && maxPriorityFeePerGas) {
|
||||
return {
|
||||
...job,
|
||||
gasPriceOptions: {
|
||||
maxFeePerGas: addExtraGas(maxFeePerGas, bumpFactor, maxGasPrice).toString(),
|
||||
maxPriorityFeePerGas: addExtraGas(maxPriorityFeePerGas, bumpFactor, maxGasPrice).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
return job
|
||||
}
|
||||
|
||||
function chooseGasPriceOptions(a, b) {
|
||||
if (!a) {
|
||||
return b
|
||||
}
|
||||
if (a && b && a.gasPrice && b.gasPrice) {
|
||||
return { gasPrice: BigNumber.max(a.gasPrice, b.gasPrice).toString() }
|
||||
}
|
||||
if (a && b && a.maxFeePerGas && b.maxFeePerGas && a.maxPriorityFeePerGas && b.maxPriorityFeePerGas) {
|
||||
return {
|
||||
maxFeePerGas: BigNumber.max(a.maxFeePerGas, b.maxFeePerGas).toString(),
|
||||
maxPriorityFeePerGas: BigNumber.max(a.maxPriorityFeePerGas, b.maxPriorityFeePerGas).toString()
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
async function setIntervalAndRun(f, interval) {
|
||||
const handler = setInterval(f, interval)
|
||||
await f()
|
||||
@ -183,6 +228,8 @@ module.exports = {
|
||||
waitForFunds,
|
||||
waitForUnsuspend,
|
||||
addExtraGas,
|
||||
chooseGasPriceOptions,
|
||||
applyMinGasFeeBump,
|
||||
setIntervalAndRun,
|
||||
watchdog,
|
||||
add0xPrefix,
|
||||
|
@ -3,7 +3,13 @@ const chai = require('chai')
|
||||
const chaiAsPromised = require('chai-as-promised')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const proxyquire = require('proxyquire')
|
||||
const { addExtraGas, syncForEach, promiseAny } = require('../src/utils/utils')
|
||||
const {
|
||||
addExtraGas,
|
||||
applyMinGasFeeBump,
|
||||
chooseGasPriceOptions,
|
||||
syncForEach,
|
||||
promiseAny
|
||||
} = require('../src/utils/utils')
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
chai.should()
|
||||
@ -173,4 +179,43 @@ describe('utils', () => {
|
||||
await promiseAny(array.map(f)).should.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('applyMinGasFeeBump', () => {
|
||||
it('should bump pre-eip1559 fee', () => {
|
||||
const job = { gasPriceOptions: { gasPrice: '100000000000' } }
|
||||
const newJob = applyMinGasFeeBump(job)
|
||||
expect(newJob.gasPriceOptions.gasPrice).to.be.equal('110000000000')
|
||||
})
|
||||
|
||||
it('should bump eip1559 fee', () => {
|
||||
const job = { gasPriceOptions: { maxFeePerGas: '100000000000', maxPriorityFeePerGas: '20000000000' } }
|
||||
const newJob = applyMinGasFeeBump(job)
|
||||
expect(newJob.gasPriceOptions.maxFeePerGas).to.be.equal('110000000000')
|
||||
expect(newJob.gasPriceOptions.maxPriorityFeePerGas).to.be.equal('22000000000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('chooseGasPriceOptions', () => {
|
||||
it('should choose max pre-eip1559 fee', () => {
|
||||
const opts1 = { gasPrice: '100000000000' }
|
||||
const opts2 = { gasPrice: '101000000000' }
|
||||
expect(chooseGasPriceOptions(opts1, opts2).gasPrice).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(opts2, opts1).gasPrice).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(opts2, undefined).gasPrice).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(undefined, opts2).gasPrice).to.be.equal('101000000000')
|
||||
})
|
||||
|
||||
it('should choose max eip1559 fee', () => {
|
||||
const opts1 = { maxFeePerGas: '100000000000', maxPriorityFeePerGas: '21000000000' }
|
||||
const opts2 = { maxFeePerGas: '101000000000', maxPriorityFeePerGas: '20000000000' }
|
||||
expect(chooseGasPriceOptions(opts1, opts2).maxFeePerGas).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(opts1, opts2).maxPriorityFeePerGas).to.be.equal('21000000000')
|
||||
expect(chooseGasPriceOptions(opts2, opts1).maxFeePerGas).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(opts2, opts1).maxPriorityFeePerGas).to.be.equal('21000000000')
|
||||
expect(chooseGasPriceOptions(opts2, undefined).maxFeePerGas).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(opts2, undefined).maxPriorityFeePerGas).to.be.equal('20000000000')
|
||||
expect(chooseGasPriceOptions(undefined, opts2).maxFeePerGas).to.be.equal('101000000000')
|
||||
expect(chooseGasPriceOptions(undefined, opts2).maxPriorityFeePerGas).to.be.equal('20000000000')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user