merge 0.4.7
This commit is contained in:
commit
e37e9c3c8d
|
@ -34,6 +34,9 @@ export interface TxManagerConfig {
|
|||
BLOCK_GAS_LIMIT?: number
|
||||
PRIORITY_FEE_GWEI?: number
|
||||
BASE_FEE_RESERVE_PERCENTAGE?: number
|
||||
ENABLE_EIP1559?: boolean
|
||||
DEFAULT_PRIORITY_FEE?: number
|
||||
PRIORITY_FEE_RESERVE_PERCENTAGE?: number
|
||||
}
|
||||
|
||||
export interface TxManagerParams {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tx-manager",
|
||||
"version": "0.4.6",
|
||||
"version": "0.4.7",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
@ -24,7 +24,7 @@
|
|||
"dependencies": {
|
||||
"async-mutex": "^0.2.4",
|
||||
"ethers": "^5.4.6",
|
||||
"gas-price-oracle": "^0.4.6",
|
||||
"gas-price-oracle": "^0.4.7",
|
||||
"web3-core-promievent": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
'use strict'
|
||||
const ethers = require('ethers')
|
||||
const { parseUnits, formatUnits } = ethers.utils
|
||||
const BigNumber = ethers.BigNumber
|
||||
|
@ -7,21 +6,27 @@ const { sleep, min, max } = require('./utils')
|
|||
|
||||
const nonceErrors = [
|
||||
'Transaction nonce is too low. Try incrementing the nonce.',
|
||||
'nonce too low',
|
||||
/nonce too low/i,
|
||||
'nonce has already been used',
|
||||
/OldNonce/,
|
||||
'invalid transaction nonce',
|
||||
]
|
||||
|
||||
const gasPriceErrors = [
|
||||
'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.',
|
||||
'replacement transaction underpriced',
|
||||
'transaction underpriced',
|
||||
/replacement transaction underpriced/i,
|
||||
/transaction underpriced/,
|
||||
/Transaction gas price \d+wei is too low. There is another transaction with same nonce in the queue with gas price: \d+wei. Try increasing the gas price or incrementing the nonce./,
|
||||
/FeeTooLow/,
|
||||
/max fee per gas less than block base fee/,
|
||||
]
|
||||
|
||||
// prettier-ignore
|
||||
const sameTxErrors = [
|
||||
'Transaction with the same hash was already imported.',
|
||||
'already known',
|
||||
'AlreadyKnown',
|
||||
'Known transaction'
|
||||
]
|
||||
|
||||
class Transaction {
|
||||
|
@ -64,13 +69,16 @@ class Transaction {
|
|||
this.tx = { ...tx }
|
||||
return
|
||||
}
|
||||
|
||||
if (!tx.gasLimit) {
|
||||
const estimatedGasLimit = await this.manager._wallet.estimateGas(tx)
|
||||
const estimatedGasLimit = await this._estimateGas(tx)
|
||||
tx.gasLimit = min(
|
||||
estimatedGasLimit.mul(this.manager.config.GAS_LIMIT_MULTIPLIER * 100).div(100),
|
||||
this.manager.config.BLOCK_GAS_LIMIT,
|
||||
)
|
||||
}
|
||||
|
||||
tx.chainId = this.tx.chainId
|
||||
tx.nonce = this.tx.nonce // can be different from `this.manager._nonce`
|
||||
|
||||
// start no less than current tx gas params
|
||||
|
@ -95,7 +103,6 @@ class Transaction {
|
|||
from: this.manager.address,
|
||||
to: this.manager.address,
|
||||
value: 0,
|
||||
gasLimit: 21000,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -131,8 +138,14 @@ class Transaction {
|
|||
this.manager.config.BLOCK_GAS_LIMIT = Math.floor(lastBlock.gasLimit.toNumber() * 0.95)
|
||||
}
|
||||
|
||||
if (!this.manager._chainId) {
|
||||
const net = await this.manager._provider.getNetwork()
|
||||
this.manager._chainId = net.chainId
|
||||
}
|
||||
this.tx.chainId = this.manager._chainId
|
||||
|
||||
if (!this.tx.gasLimit || this.manager.config.ESTIMATE_GAS) {
|
||||
const gas = await this.manager._wallet.estimateGas(this.tx)
|
||||
const gas = await this._estimateGas(this.tx)
|
||||
if (!this.tx.gasLimit) {
|
||||
const gasLimit = Math.floor(gas * this.manager.config.GAS_LIMIT_MULTIPLIER)
|
||||
this.tx.gasLimit = Math.min(gasLimit, this.manager.config.BLOCK_GAS_LIMIT)
|
||||
|
@ -144,12 +157,6 @@ class Transaction {
|
|||
}
|
||||
this.tx.nonce = this.manager._nonce
|
||||
|
||||
if (!this.manager._chainId) {
|
||||
const net = await this.manager._provider.getNetwork()
|
||||
this.manager._chainId = net.chainId
|
||||
}
|
||||
this.tx.chainId = this.manager._chainId
|
||||
|
||||
if (this.tx.gasPrice || (this.tx.maxFeePerGas && this.tx.maxPriorityFeePerGas)) {
|
||||
return
|
||||
}
|
||||
|
@ -175,7 +182,7 @@ class Transaction {
|
|||
try {
|
||||
await this._broadcast(signedTx)
|
||||
} catch (e) {
|
||||
return this._handleSendError(e)
|
||||
return this._handleRpcError(e, '_send')
|
||||
}
|
||||
|
||||
this._emitter.emit('transactionHash', txHash)
|
||||
|
@ -293,7 +300,7 @@ class Transaction {
|
|||
return main
|
||||
}
|
||||
|
||||
_handleSendError(e) {
|
||||
_handleRpcError(e, method) {
|
||||
if (e.error.error) {
|
||||
// Sometimes ethers wraps known errors, unwrap it in this case
|
||||
e = e.error
|
||||
|
@ -308,17 +315,20 @@ class Transaction {
|
|||
if (this.retries <= this.manager.config.MAX_RETRIES) {
|
||||
this.tx.nonce++
|
||||
this.retries++
|
||||
return this._send()
|
||||
return this[method]()
|
||||
}
|
||||
}
|
||||
|
||||
// there is already a pending tx with higher gas price, trying to bump and resubmit
|
||||
if (this._hasError(message, gasPriceErrors)) {
|
||||
console.log(
|
||||
`Gas price ${formatUnits(this.tx.gasPrice, 'gwei')} gwei is too low, increasing and retrying`,
|
||||
`Gas price ${formatUnits(
|
||||
this.tx.gasPrice || this.tx.maxFeePerGas,
|
||||
'gwei',
|
||||
)} gwei is too low, increasing and retrying`,
|
||||
)
|
||||
if (this._increaseGasPrice()) {
|
||||
return this._send()
|
||||
return this[method]()
|
||||
} else {
|
||||
throw new Error('Already at max gas price, but still not enough to submit the transaction')
|
||||
}
|
||||
|
@ -379,8 +389,10 @@ class Transaction {
|
|||
oldMaxPriorityFeePerGas.add(minGweiBump),
|
||||
)
|
||||
|
||||
this.tx.maxFeePerGas = min(newMaxFeePerGas, maxGasPrice).toHexString()
|
||||
this.tx.maxPriorityFeePerGas = min(newMaxPriorityFeePerGas, this.tx.maxFeePerGas).toHexString()
|
||||
const maxFeePerGas = min(newMaxFeePerGas, maxGasPrice)
|
||||
|
||||
this.tx.maxFeePerGas = maxFeePerGas.toHexString()
|
||||
this.tx.maxPriorityFeePerGas = min(newMaxPriorityFeePerGas, maxFeePerGas).toHexString()
|
||||
|
||||
console.log(`Increasing maxFeePerGas to ${formatUnits(this.tx.maxFeePerGas, 'gwei')} gwei`)
|
||||
}
|
||||
|
@ -412,6 +424,33 @@ class Transaction {
|
|||
return this.manager._wallet.getTransactionCount('latest')
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches priority fee from the chain
|
||||
*
|
||||
* @returns {Promise<BigNumber>}
|
||||
* @private
|
||||
*/
|
||||
async _estimatePriorityFee() {
|
||||
const defaultPriorityFee = parseUnits(this.manager.config.DEFAULT_PRIORITY_FEE.toString(), 'gwei')
|
||||
|
||||
try {
|
||||
const estimatedPriorityFee = await this.manager._provider.send('eth_maxPriorityFeePerGas', [])
|
||||
|
||||
if (!estimatedPriorityFee || isNaN(estimatedPriorityFee)) {
|
||||
return defaultPriorityFee
|
||||
}
|
||||
|
||||
const bumpedPriorityFee = BigNumber.from(estimatedPriorityFee)
|
||||
.mul(100 + this.manager.config.PRIORITY_FEE_RESERVE_PERCENTAGE)
|
||||
.div(100)
|
||||
|
||||
return max(bumpedPriorityFee, defaultPriorityFee)
|
||||
} catch (err) {
|
||||
console.error('_estimatePriorityFee has error:', err.message)
|
||||
return defaultPriorityFee
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose network gas params
|
||||
*
|
||||
|
@ -423,12 +462,14 @@ class Transaction {
|
|||
const block = await this.manager._provider.getBlock('latest')
|
||||
|
||||
// Check network support for EIP-1559
|
||||
if (block && block.baseFeePerGas) {
|
||||
const maxPriorityFeePerGas = parseUnits(this.manager.config.PRIORITY_FEE_GWEI.toString(), 'gwei')
|
||||
if (this.manager.config.ENABLE_EIP1559 && block && block.baseFeePerGas) {
|
||||
const maxPriorityFeePerGas = await this._estimatePriorityFee()
|
||||
|
||||
const maxFeePerGas = block.baseFeePerGas
|
||||
.mul(100 + this.manager.config.BASE_FEE_RESERVE_PERCENTAGE)
|
||||
.div(100)
|
||||
.add(maxPriorityFeePerGas)
|
||||
|
||||
return {
|
||||
maxFeePerGas: min(maxFeePerGas, maxGasPrice).toHexString(),
|
||||
maxPriorityFeePerGas: min(maxPriorityFeePerGas, maxGasPrice).toHexString(),
|
||||
|
@ -442,6 +483,14 @@ class Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _estimateGas(tx) {
|
||||
try {
|
||||
return await this.manager._wallet.estimateGas(tx)
|
||||
} catch (e) {
|
||||
return this._handleRpcError(e, '_estimateGas')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transaction
|
||||
|
|
|
@ -15,8 +15,10 @@ const defaultConfig = {
|
|||
ESTIMATE_GAS: true,
|
||||
THROW_ON_REVERT: true,
|
||||
BLOCK_GAS_LIMIT: null,
|
||||
PRIORITY_FEE_GWEI: 3,
|
||||
ENABLE_EIP1559: true,
|
||||
DEFAULT_PRIORITY_FEE: 3,
|
||||
BASE_FEE_RESERVE_PERCENTAGE: 50,
|
||||
PRIORITY_FEE_RESERVE_PERCENTAGE: 10,
|
||||
}
|
||||
|
||||
class TxManager {
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
require('dotenv').config()
|
||||
require('chai').should()
|
||||
const { providers } = require('ethers')
|
||||
const { parseUnits } = require('ethers').utils
|
||||
const TxManager = require('../src/TxManager')
|
||||
// const Transaction = require('../src/Transaction')
|
||||
const { RPC_URL, PRIVATE_KEY } = process.env
|
||||
|
||||
describe('TxManager', () => {
|
||||
const manager = new TxManager({
|
||||
privateKey: PRIVATE_KEY,
|
||||
rpcUrl: RPC_URL,
|
||||
config: {
|
||||
CONFIRMATIONS: 1,
|
||||
GAS_BUMP_INTERVAL: 1000 * 20,
|
||||
},
|
||||
})
|
||||
let manager
|
||||
|
||||
const tx1 = {
|
||||
value: 1,
|
||||
|
@ -45,6 +39,26 @@ describe('TxManager', () => {
|
|||
type: 2,
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
const provider = new providers.JsonRpcProvider(RPC_URL)
|
||||
|
||||
const { name, chainId } = await provider.getNetwork()
|
||||
console.log('\n\n', 'network', { name, chainId }, '\n\n')
|
||||
|
||||
manager = new TxManager({
|
||||
privateKey: PRIVATE_KEY,
|
||||
rpcUrl: RPC_URL,
|
||||
config: {
|
||||
CONFIRMATIONS: 1,
|
||||
GAS_BUMP_INTERVAL: 1000 * 20,
|
||||
},
|
||||
gasPriceOracleConfig: {
|
||||
chainId: chainId,
|
||||
defaultRpc: RPC_URL,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('#transaction', () => {
|
||||
it('should work legacy tx', async () => {
|
||||
const tx = manager.createTx(tx1)
|
||||
|
@ -122,6 +136,36 @@ describe('TxManager', () => {
|
|||
console.log('receipt', receipt)
|
||||
})
|
||||
|
||||
it('should increase nonce', async () => {
|
||||
const currentNonce = await manager._wallet.getTransactionCount('latest')
|
||||
|
||||
manager._nonce = currentNonce - 1
|
||||
|
||||
const tx = manager.createTx(tx4)
|
||||
|
||||
const receipt = await tx
|
||||
.send()
|
||||
.on('transactionHash', hash => console.log('hash', hash))
|
||||
.on('mined', receipt => console.log('Mined in block', receipt.blockNumber))
|
||||
.on('confirmations', confirmations => console.log('confirmations', confirmations))
|
||||
|
||||
console.log('receipt', receipt)
|
||||
})
|
||||
|
||||
it('should disable eip-1559 transactions', async () => {
|
||||
manager.config.ENABLE_EIP1559 = false
|
||||
|
||||
const tx = manager.createTx(tx3)
|
||||
const receipt = await tx
|
||||
.send()
|
||||
.on('transactionHash', hash => console.log('hash', hash))
|
||||
.on('mined', receipt => console.log('Mined in block', receipt.blockNumber))
|
||||
.on('confirmations', confirmations => console.log('confirmations', confirmations))
|
||||
console.log('receipt', receipt)
|
||||
|
||||
manager.config.ENABLE_EIP1559 = true
|
||||
})
|
||||
|
||||
it('should send multiple txs', async () => {
|
||||
const genTx = value => ({
|
||||
value,
|
||||
|
@ -139,6 +183,6 @@ describe('TxManager', () => {
|
|||
manager.createTx(genTx(9)).send(),
|
||||
manager.createTx(genTx(10)).send(),
|
||||
])
|
||||
})
|
||||
}).timeout(600000)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1056,10 +1056,10 @@ functional-red-black-tree@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
gas-price-oracle@^0.4.6:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/gas-price-oracle/-/gas-price-oracle-0.4.6.tgz#3e496092122896f1c80ea7eeeecea979d106b3aa"
|
||||
integrity sha512-/z0wtzKa6FDTWmgikPnELWN8KiPHhCy3Z+waeKVMgvs5FBxibgwOUL1VlMsC4mVkXBoDadnBtFNOpUMgbt5pvg==
|
||||
gas-price-oracle@^0.4.7:
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/gas-price-oracle/-/gas-price-oracle-0.4.7.tgz#47406048083074bcab677efb9de08663e742153d"
|
||||
integrity sha512-Ti8nhpATm83YebWU/Pz5xclZoTkzOblIhT504ZViZJUcd8jOxgj9pWtCasg8RYw+d0f19m0dJUPvdj04RC4o3A==
|
||||
dependencies:
|
||||
axios "^0.21.2"
|
||||
bignumber.js "^9.0.0"
|
||||
|
|
Loading…
Reference in New Issue