Skip to content

Commit

Permalink
Merge pull request #1 from crossee/Support-EIP-1559-gas-level
Browse files Browse the repository at this point in the history
Support eip 1559 gas level
  • Loading branch information
crossee authored Oct 24, 2022
2 parents 06b380b + 4077c0a commit 70fb359
Show file tree
Hide file tree
Showing 23 changed files with 705 additions and 93 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gas-price-oracle",
"version": "0.5.1",
"version": "0.5.2",
"description": "Gas Price Oracle library for Ethereum dApps.",
"homepage": "https://github.com/peppersec/gas-price-oracle",
"main": "./lib/index.js",
Expand Down
28 changes: 14 additions & 14 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import bscOracles from './bsc'
import xdaiOracles from './xdai'
import avalancheOracles from './avax'
import mainnetOracles from './mainnet'
import polygonOracles from './polygon'
import optimismOracles from './optimism'
import arbitrumOracles from './arbitrum'
import bscOracles from '@/config/bsc'
import xdaiOracles from '@/config/xdai'
import avalancheOracles from '@/config/avax'
import mainnetOracles from '@/config/mainnet'
import polygonOracles from '@/config/polygon'
import optimismOracles from '@/config/optimism'
import arbitrumOracles from '@/config/arbitrum'

import { NetworksConfig } from '@/types'

Expand All @@ -24,55 +24,55 @@ export const NETWORKS: Record<number, NetworksConfig> = {
rpcUrl: 'https://api.mycryptoapi.com/eth',
defaultGasPrice: 22,
maxGasPrice: 1500,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
[ChainId.BSC]: {
oracles: bscOracles,
rpcUrl: 'https://bsc-dataseed1.ninicoin.io',
defaultGasPrice: 5,
maxGasPrice: 200,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
[ChainId.XDAI]: {
oracles: xdaiOracles,
rpcUrl: 'https://rpc.gnosischain.com',
defaultGasPrice: 5,
maxGasPrice: 200,
blocksCount: 200,
blocksCount: 30,
percentile: 5,
},
[ChainId.POLYGON]: {
oracles: polygonOracles,
rpcUrl: 'https://rpc-mainnet.maticvigil.com',
defaultGasPrice: 75,
maxGasPrice: 1000,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
[ChainId.OPTIMISM]: {
oracles: optimismOracles,
rpcUrl: 'https://mainnet.optimism.io',
defaultGasPrice: 0.001,
maxGasPrice: 5,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
[ChainId.ARBITRUM]: {
oracles: arbitrumOracles,
rpcUrl: 'https://arb1.arbitrum.io/rpc',
defaultGasPrice: 3,
maxGasPrice: 15,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
[ChainId.AVAX]: {
oracles: avalancheOracles,
rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
defaultGasPrice: 50,
maxGasPrice: 1000,
blocksCount: 10,
blocksCount: 30,
percentile: 5,
},
}
2 changes: 1 addition & 1 deletion src/services/cacher/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './cacheNode'
export * from '@/services/cacher/cacheNode'
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import BigNumber from 'bignumber.js'
import { fromWeiToGwei } from '../../utils'
import { Eip1559GasFee, GasFeeEstimates, FeeHistoryBlock } from './types'
import medianOf from './medianOf'

export type PriorityLevel = typeof PRIORITY_LEVELS[number]
export type Percentile = typeof PRIORITY_LEVEL_PERCENTILES[number]

const PRIORITY_LEVELS = ['low', 'medium', 'high'] as const
const PRIORITY_LEVEL_PERCENTILES = [10, 20, 30] as const
const SETTINGS_BY_PRIORITY_LEVEL = {
low: {
percentile: 10 as Percentile,
baseFeePercentageMultiplier: new BigNumber(110),
priorityFeePercentageMultiplier: new BigNumber(94),
minSuggestedMaxPriorityFeePerGas: new BigNumber(1_000_000_000),
estimatedWaitTimes: {
minWaitTimeEstimate: 15_000,
maxWaitTimeEstimate: 30_000,
},
},
medium: {
percentile: 20 as Percentile,
baseFeePercentageMultiplier: new BigNumber(120),
priorityFeePercentageMultiplier: new BigNumber(97),
minSuggestedMaxPriorityFeePerGas: new BigNumber(1_500_000_000),
estimatedWaitTimes: {
minWaitTimeEstimate: 15_000,
maxWaitTimeEstimate: 45_000,
},
},
high: {
percentile: 30 as Percentile,
baseFeePercentageMultiplier: new BigNumber(125),
priorityFeePercentageMultiplier: new BigNumber(98),
minSuggestedMaxPriorityFeePerGas: new BigNumber(2_000_000_000),
estimatedWaitTimes: {
minWaitTimeEstimate: 15_000,
maxWaitTimeEstimate: 60_000,
},
},
}

/**
* Calculates a set of estimates assigned to a particular priority level based on the data returned
* by `eth_feeHistory`.
*
* @param priorityLevel - The level of fees that dictates how soon a transaction may go through
* ("low", "medium", or "high").
* @param blocks - A set of blocks as obtained from {@link fetchBlockFeeHistory}.
* @returns The estimates.
*/
function calculateEstimatesForPriorityLevel(priorityLevel: PriorityLevel, blocks: FeeHistoryBlock<Percentile>[]): Eip1559GasFee {
const settings = SETTINGS_BY_PRIORITY_LEVEL[priorityLevel]

const latestBaseFeePerGas = blocks[blocks.length - 1].baseFeePerGas

const adjustedBaseFee = latestBaseFeePerGas.multipliedBy(settings.baseFeePercentageMultiplier).dividedBy(100)
const priorityFees = blocks
.map((block) => {
return 'priorityFeesByPercentile' in block ? block.priorityFeesByPercentile[settings.percentile] : null
})
.filter(BigNumber.isBigNumber)
const medianPriorityFee = medianOf(priorityFees)
const adjustedPriorityFee = medianPriorityFee.multipliedBy(settings.priorityFeePercentageMultiplier).dividedBy(100)

const suggestedMaxPriorityFeePerGas = BigNumber.maximum(adjustedPriorityFee, settings.minSuggestedMaxPriorityFeePerGas)
const suggestedMaxFeePerGas = adjustedBaseFee.plus(suggestedMaxPriorityFeePerGas)

return {
...settings.estimatedWaitTimes,
suggestedMaxPriorityFeePerGas: fromWeiToGwei(suggestedMaxPriorityFeePerGas).toString(10),
suggestedMaxFeePerGas: fromWeiToGwei(suggestedMaxFeePerGas).toString(10),
}
}

/**
* Calculates a set of estimates suitable for different priority levels based on the data returned
* by `eth_feeHistory`.
*
* @param blocks - A set of blocks populated with data for priority fee percentiles 10, 20, and 30,
* obtained via {@link BlockFeeHistoryDatasetFetcher}.
* @returns The estimates.
*/
export default function calculateGasFeeEstimatesForPriorityLevels(
blocks: FeeHistoryBlock<Percentile>[],
): Pick<GasFeeEstimates, PriorityLevel> {
return PRIORITY_LEVELS.reduce((obj, priorityLevel) => {
const gasEstimatesForPriorityLevel = calculateEstimatesForPriorityLevel(priorityLevel, blocks)
return { ...obj, [priorityLevel]: gasEstimatesForPriorityLevel }
}, {} as Pick<GasFeeEstimates, PriorityLevel>)
}
2 changes: 1 addition & 1 deletion src/services/gas-estimation/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// How many blocks to consider for priority fee estimation
import BigNumber from 'bignumber.js'
import { EstimatedGasPrice } from './types'
import { EstimatedGasPrice } from '@/services/gas-estimation/types'

const FEE_HISTORY_BLOCKS = 10
// Which percentile of effective priority fees to include
Expand Down
42 changes: 40 additions & 2 deletions src/services/gas-estimation/eip1559.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import BigNumber from 'bignumber.js'

import { FeeHistory, Block } from '@/types'
import { Config, EstimateOracle, EstimatedGasPrice, CalculateFeesParams, GasEstimationOptionsPayload } from './types'
import {
Config,
EstimateOracle,
EstimatedGasPrice,
CalculateFeesParams,
GasEstimationOptionsPayload,
GasFeeEstimates,
} from '@/services/gas-estimation/types'

import { ChainId, NETWORKS } from '@/config'
import { RpcFetcher, NodeJSCache } from '@/services'
import { findMax, fromNumberToHex, fromWeiToGwei, getMedian } from '@/utils'
import { BG_ZERO, DEFAULT_BLOCK_DURATION, PERCENT_MULTIPLIER } from '@/constants'

import { DEFAULT_PRIORITY_FEE, PRIORITY_FEE_INCREASE_BOUNDARY, FEE_HISTORY_BLOCKS, FEE_HISTORY_PERCENTILE } from './constants'
import {
DEFAULT_PRIORITY_FEE,
PRIORITY_FEE_INCREASE_BOUNDARY,
FEE_HISTORY_BLOCKS,
FEE_HISTORY_PERCENTILE,
} from '@/services/gas-estimation/constants'

import fetchGasEstimatesViaEthFeeHistory from '@/services/gas-estimation/fetchGasEstimatesViaEthFeeHistory'

// !!! MAKE SENSE ALL CALCULATIONS IN GWEI !!!
export class Eip1559GasPriceOracle implements EstimateOracle {
Expand All @@ -23,7 +37,9 @@ export class Eip1559GasPriceOracle implements EstimateOracle {
private fetcher: RpcFetcher

private cache: NodeJSCache<EstimatedGasPrice>
private perSpeedCache: NodeJSCache<GasFeeEstimates>
private FEES_KEY = (chainId: ChainId) => `estimate-fee-${chainId}`
private FEES_PER_SPEED_KEY = (chainId: ChainId) => `estimate-fee-per-speed-${chainId}`

constructor({ fetcher, ...options }: GasEstimationOptionsPayload) {
this.fetcher = fetcher
Expand All @@ -36,6 +52,7 @@ export class Eip1559GasPriceOracle implements EstimateOracle {
}

this.cache = new NodeJSCache({ stdTTL: this.configuration.blockTime, useClones: false })
this.perSpeedCache = new NodeJSCache({ stdTTL: this.configuration.blockTime, useClones: false })
}

public async estimateFees(fallbackGasPrices?: EstimatedGasPrice): Promise<EstimatedGasPrice> {
Expand Down Expand Up @@ -153,4 +170,25 @@ export class Eip1559GasPriceOracle implements EstimateOracle {
private checkIsGreaterThanMax(value: BigNumber): boolean {
return value.isGreaterThanOrEqualTo(NETWORKS[this.configuration.chainId]?.maxGasPrice) || false
}

public async estimateFeesPerSpeed(): Promise<GasFeeEstimates | void> {
const cacheKey = this.FEES_PER_SPEED_KEY(this.configuration.chainId)
const cachedFees = await this.perSpeedCache.get(cacheKey)

if (cachedFees) {
return cachedFees
}

const estimates: GasFeeEstimates = await fetchGasEstimatesViaEthFeeHistory(this.fetcher)

if (this.configuration.shouldCache && estimates) {
await this.perSpeedCache.set(cacheKey, estimates)
}

// time estimated
// const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = estimates.medium
// const estimatedGasFeeTimeBounds = calculateTimeEstimate(suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas, estimates)

return estimates
}
}
Loading

0 comments on commit 70fb359

Please sign in to comment.