remove Object.assign(), use BigNumber

This commit is contained in:
smart_ex 2022-05-12 20:40:55 +10:00
parent 6db517cde9
commit 1b59ac8923
4 changed files with 59 additions and 51 deletions

14
index.d.ts vendored
View File

@ -45,15 +45,15 @@ export interface TxManagerParams {
}
export class TxManager {
private _privateKey: string
config: TxManagerConfig
address: string
private _privateKey: string
private _provider: providers.JsonRpcProvider
private _wallet: Wallet
private _broadcastNodes: string[]
private _gasPriceOracle: GasPriceOracle
private _mutex: Mutex
private _nonce: number
_provider: providers.JsonRpcProvider
_wallet: Wallet
_broadcastNodes: string[]
_gasPriceOracle: GasPriceOracle
_mutex: Mutex
_nonce: number
constructor(params?: TxManagerParams)

View File

@ -1,3 +1,4 @@
'use strict'
const ethers = require('ethers')
const { parseUnits, formatUnits } = ethers.utils
const BigNumber = ethers.BigNumber
@ -25,7 +26,6 @@ const sameTxErrors = [
class Transaction {
constructor(tx, manager) {
Object.assign(this, manager)
this.manager = manager
this.tx = { ...tx }
this._promise = PromiEvent()
@ -64,18 +64,18 @@ class Transaction {
return
}
if (!tx.gasLimit) {
tx.gasLimit = await this._wallet.estimateGas(tx)
tx.gasLimit = Math.floor(tx.gasLimit * this.config.GAS_LIMIT_MULTIPLIER)
tx.gasLimit = Math.min(tx.gasLimit, this.config.BLOCK_GAS_LIMIT)
tx.gasLimit = await this.manager._wallet.estimateGas(tx)
tx.gasLimit = Math.floor(tx.gasLimit.mul(this.manager.config.GAS_LIMIT_MULTIPLIER).toNumber())
tx.gasLimit = min(tx.gasLimit, this.manager.config.BLOCK_GAS_LIMIT)
}
tx.nonce = this.tx.nonce // can be different from `this.manager._nonce`
// start no less than current tx gas params
if (this.tx.gasPrice) {
tx.gasPrice = Math.max(this.tx.gasPrice, tx.gasPrice || 0)
tx.gasPrice = max(this.tx.gasPrice, tx.gasPrice || 0)
} else {
tx.maxFeePerGas = Math.max(this.tx.maxFeePerGas, tx.maxFeePerGas || 0)
tx.maxPriorityFeePerGas = Math.max(this.tx.maxPriorityFeePerGas, tx.maxPriorityFeePerGas || 0)
tx.maxFeePerGas = max(this.tx.maxFeePerGas, tx.maxFeePerGas || 0)
tx.maxPriorityFeePerGas = max(this.tx.maxPriorityFeePerGas, tx.maxPriorityFeePerGas || 0)
}
this.tx = { ...tx }
@ -89,8 +89,8 @@ class Transaction {
cancel() {
console.log('Canceling the transaction')
return this.replace({
from: this.address,
to: this.address,
from: this.manager.address,
to: this.manager.address,
value: 0,
gasLimit: 21000,
})
@ -123,16 +123,16 @@ class Transaction {
* @private
*/
async _prepare() {
if (!this.config.BLOCK_GAS_LIMIT) {
const lastBlock = await this._provider.getBlock('latest')
this.config.BLOCK_GAS_LIMIT = Math.floor(lastBlock.gasLimit.toNumber() * 0.95)
if (!this.manager.config.BLOCK_GAS_LIMIT) {
const lastBlock = await this.manager._provider.getBlock('latest')
this.manager.config.BLOCK_GAS_LIMIT = Math.floor(lastBlock.gasLimit.toNumber() * 0.95)
}
if (!this.tx.gasLimit || this.config.ESTIMATE_GAS) {
const gas = await this._wallet.estimateGas(this.tx)
if (!this.tx.gasLimit || this.manager.config.ESTIMATE_GAS) {
const gas = await this.manager._wallet.estimateGas(this.tx)
if (!this.tx.gasLimit) {
const gasLimit = Math.floor(gas * this.config.GAS_LIMIT_MULTIPLIER)
this.tx.gasLimit = Math.min(gasLimit, this.config.BLOCK_GAS_LIMIT)
const gasLimit = Math.floor(gas * this.manager.config.GAS_LIMIT_MULTIPLIER)
this.tx.gasLimit = Math.min(gasLimit, this.manager.config.BLOCK_GAS_LIMIT)
}
}
@ -142,7 +142,7 @@ class Transaction {
this.tx.nonce = this.manager._nonce
if (!this.manager._chainId) {
const net = await this._provider.getNetwork()
const net = await this.manager._provider.getNetwork()
this.manager._chainId = net.chainId
}
this.tx.chainId = this.manager._chainId
@ -164,7 +164,7 @@ class Transaction {
*/
async _send() {
// todo throw is we attempt to send a tx that attempts to replace already mined tx
const signedTx = await this._wallet.signTransaction(this.tx)
const signedTx = await this.manager._wallet.signTransaction(this.tx)
this.submitTimestamp = Date.now()
const txHash = ethers.utils.keccak256(signedTx)
this.hashes.push(txHash)
@ -190,7 +190,7 @@ class Transaction {
while (true) {
// We are already waiting on certain tx hash
if (this.currentTxHash) {
const receipt = await this._provider.getTransactionReceipt(this.currentTxHash)
const receipt = await this.manager._provider.getTransactionReceipt(this.currentTxHash)
if (!receipt) {
// We were waiting for some tx but it disappeared
@ -199,20 +199,20 @@ class Transaction {
continue
}
const currentBlock = await this._provider.getBlockNumber()
const currentBlock = await this.manager._provider.getBlockNumber()
const confirmations = Math.max(0, currentBlock - receipt.blockNumber)
// todo don't emit repeating confirmation count
this._emitter.emit('confirmations', confirmations)
if (confirmations >= this.config.CONFIRMATIONS) {
if (confirmations >= this.manager.config.CONFIRMATIONS) {
// Tx is mined and has enough confirmations
if (this.config.THROW_ON_REVERT && Number(receipt.status) === 0) {
if (this.manager.config.THROW_ON_REVERT && Number(receipt.status) === 0) {
throw new Error('EVM execution failed, so the transaction was reverted.')
}
return receipt
}
// Tx is mined but doesn't have enough confirmations yet, keep waiting
await sleep(this.config.POLL_INTERVAL)
await sleep(this.manager.config.POLL_INTERVAL)
continue
}
@ -221,7 +221,7 @@ class Transaction {
// todo optionally run estimateGas on each iteration and cancel the transaction if it fails
// We were waiting too long, increase gas price and resubmit
if (Date.now() - this.submitTimestamp >= this.config.GAS_BUMP_INTERVAL) {
if (Date.now() - this.submitTimestamp >= this.manager.config.GAS_BUMP_INTERVAL) {
if (this._increaseGasPrice()) {
console.log('Resubmitting with higher gas params')
await this._send()
@ -229,7 +229,7 @@ class Transaction {
}
}
// Tx is still pending, keep waiting
await sleep(this.config.POLL_INTERVAL)
await sleep(this.manager.config.POLL_INTERVAL)
continue
}
@ -267,7 +267,7 @@ class Transaction {
async _getReceipts() {
for (const hash of this.hashes.reverse()) {
const receipt = await this._provider.getTransactionReceipt(hash)
const receipt = await this.manager._provider.getTransactionReceipt(hash)
if (receipt) {
return receipt
}
@ -278,11 +278,11 @@ class Transaction {
/**
* Broadcasts tx to multiple nodes, waits for tx hash only on main node
*/
_broadcast(rawTx) {
const main = this._provider.sendTransaction(rawTx)
for (const node of this._broadcastNodes) {
async _broadcast(rawTx) {
const main = await this.manager._provider.sendTransaction(rawTx)
for (const node of this.manager._broadcastNodes) {
try {
new ethers.providers.JsonRpcProvider(node).sendTransaction(rawTx)
await new ethers.providers.JsonRpcProvider(node).sendTransaction(rawTx)
} catch (e) {
console.log(`Failed to send transaction to node ${node}: ${e}`)
}
@ -302,7 +302,7 @@ class Transaction {
// nonce is too low, trying to increase and resubmit
if (this._hasError(message, nonceErrors)) {
console.log(`Nonce ${this.tx.nonce} is too low, increasing and retrying`)
if (this.retries <= this.config.MAX_RETRIES) {
if (this.retries <= this.manager.config.MAX_RETRIES) {
this.tx.nonce++
this.retries++
return this._send()
@ -343,8 +343,8 @@ class Transaction {
}
_increaseGasPrice() {
const maxGasPrice = parseUnits(this.config.MAX_GAS_PRICE.toString(), 'gwei')
const minGweiBump = parseUnits(this.config.MIN_GWEI_BUMP.toString(), 'gwei')
const maxGasPrice = parseUnits(this.manager.config.MAX_GAS_PRICE.toString(), 'gwei')
const minGweiBump = parseUnits(this.manager.config.MIN_GWEI_BUMP.toString(), 'gwei')
if (this.tx.gasPrice) {
const oldGasPrice = BigNumber.from(this.tx.gasPrice)
@ -354,7 +354,7 @@ class Transaction {
}
const newGasPrice = max(
oldGasPrice.mul(100 + this.config.GAS_BUMP_PERCENTAGE).div(100),
oldGasPrice.mul(100 + this.manager.config.GAS_BUMP_PERCENTAGE).div(100),
oldGasPrice.add(minGweiBump),
)
this.tx.gasPrice = min(newGasPrice, maxGasPrice).toHexString()
@ -368,11 +368,11 @@ class Transaction {
}
const newMaxFeePerGas = max(
oldMaxFeePerGas.mul(100 + this.config.GAS_BUMP_PERCENTAGE).div(100),
oldMaxFeePerGas.mul(100 + this.manager.config.GAS_BUMP_PERCENTAGE).div(100),
oldMaxFeePerGas.add(minGweiBump),
)
const newMaxPriorityFeePerGas = max(
oldMaxPriorityFeePerGas.mul(100 + this.config.GAS_BUMP_PERCENTAGE).div(100),
oldMaxPriorityFeePerGas.mul(100 + this.manager.config.GAS_BUMP_PERCENTAGE).div(100),
oldMaxPriorityFeePerGas.add(minGweiBump),
)
@ -393,7 +393,7 @@ class Transaction {
* @private
*/
async _getGasPrice(type) {
const gasPrices = await this._gasPriceOracle.gasPrices()
const gasPrices = await this.manager._gasPriceOracle.gasPrices()
const result = gasPrices[type].toString()
console.log(`${type} gas price is now ${result} gwei`)
return parseUnits(result, 'gwei').toHexString()
@ -406,7 +406,7 @@ class Transaction {
* @private
*/
_getLastNonce() {
return this._wallet.getTransactionCount('latest')
return this.manager._wallet.getTransactionCount('latest')
}
/**
@ -416,14 +416,14 @@ class Transaction {
* @private
*/
async _getGasParams() {
const maxGasPrice = parseUnits(this.config.MAX_GAS_PRICE.toString(), 'gwei')
const block = await this._provider.getBlock('latest')
const maxGasPrice = parseUnits(this.manager.config.MAX_GAS_PRICE.toString(), 'gwei')
const block = await this.manager._provider.getBlock('latest')
// Check network support for EIP-1559
if (block && block.baseFeePerGas) {
const maxPriorityFeePerGas = parseUnits(this.config.PRIORITY_FEE_GWEI.toString(), 'gwei')
const maxPriorityFeePerGas = parseUnits(this.manager.config.PRIORITY_FEE_GWEI.toString(), 'gwei')
const maxFeePerGas = block.baseFeePerGas
.mul(100 + this.config.BASE_FEE_RESERVE_PERCENTAGE)
.mul(100 + this.manager.config.BASE_FEE_RESERVE_PERCENTAGE)
.div(100)
.add(maxPriorityFeePerGas)
return {

7
src/utils.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import { BigNumberish } from 'ethers'
export function sleep(ms: number): Promise<any>
export function max(a: BigNumberish, b: BigNumberish): BigNumberish
export function min(a: BigNumberish, b: BigNumberish): BigNumberish

View File

@ -1,11 +1,12 @@
const { BigNumber } = require('ethers')
/**
* A promise that resolves after `ms` milliseconds
*/
const sleep = ms => new Promise(res => setTimeout(res, ms))
const max = (a, b) => (a.gt(b) ? a : b)
const max = (a, b) => (BigNumber.from(a).gt(b) ? a : b)
const min = (a, b) => (a.lt(b) ? a : b)
const min = (a, b) => (BigNumber.from(a).lt(b) ? a : b)
module.exports = {
sleep,