import BigNumber from 'bignumber.js' ; import { ProviderHandler } from '@services/EthService' ; import { bigify , fromWei , getDecimalFromEtherUnit , hexlify , toWei } from '@utils' ; import { MAX_GAS_FAST } from './constants' ; const FEE_HISTORY_BLOCKS = 10 ; const FEE_HISTORY_PERCENTILE = 5 ; const PRIORITY_FEE_ESTIMATION_TRIGGER = 100 ; const DEFAULT_PRIORITY_FEE = bigify ( toWei ( '3' , getDecimalFromEtherUnit ( 'gwei' ) ) ) ; export const FALLBACK_ESTIMATE = { maxFeePerGas : bigify ( toWei ( '20' , getDecimalFromEtherUnit ( 'gwei' ) ) ) , maxPriorityFeePerGas : DEFAULT_PRIORITY_FEE , baseFee : undefined } ; const PRIORITY_FEE_INCREASE_BOUNDARY = 200 ; const toGwei = ( wei ) => bigify ( fromWei ( wei . integerValue ( ) , 'gwei' ) ) ; const roundToWholeGwei = ( wei ) => { const gwei = toGwei ( wei ) ; const rounded = gwei . integerValue ( BigNumber . ROUND_HALF_UP ) ; return bigify ( toWei ( rounded . toString ( 10 ) , getDecimalFromEtherUnit ( 'gwei' ) ) ) ; } ; const getBaseFeeMultiplier = ( baseFeeGwei ) => { if ( baseFeeGwei . lte ( 40 ) ) { return bigify ( 2.0 ) ; } else if ( baseFeeGwei . lte ( 100 ) ) { return bigify ( 1.6 ) ; } else if ( baseFeeGwei . lte ( 200 ) ) { return bigify ( 1.4 ) ; } else { return bigify ( 1.2 ) ; } } ; const estimatePriorityFee = async ( provider , baseFeeGwei , blockNumber ) => { if ( baseFeeGwei . lt ( PRIORITY_FEE_ESTIMATION_TRIGGER ) ) { return DEFAULT_PRIORITY_FEE ; } const feeHistory = await provider . getFeeHistory ( hexlify ( FEE_HISTORY_BLOCKS ) , hexlify ( blockNumber ) , [ FEE_HISTORY_PERCENTILE ] ) ; const rewards = feeHistory . reward ?. map ( ( r ) => bigify ( r [ 0 ] ) ) . filter ( ( r ) => ! r . isZero ( ) ) . sort ( ) ; if ( ! rewards ) { return null ; } const percentageIncreases = rewards . reduce ( ( acc , cur , i , arr ) => { if ( i === arr . length - 1 ) { return acc ; } const next = arr [ i + 1 ] ; const p = next . minus ( cur ) . dividedBy ( cur ) . multipliedBy ( 100 ) ; return [ ... acc , p ] ; } , [ ] ) ; const highestIncrease = BigNumber . max ( ... percentageIncreases ) ; const highestIncreaseIndex = percentageIncreases . findIndex ( ( p ) => p . eq ( highestIncrease ) ) ; const values = highestIncrease . gte ( PRIORITY_FEE_INCREASE_BOUNDARY ) && highestIncreaseIndex >= Math . floor ( rewards . length / 2 ) ? rewards . slice ( highestIncreaseIndex ) : rewards ; return values [ Math . floor ( values . length / 2 ) ] ; } ; export const estimateFees = async ( provider ) => { try { const latestBlock = await provider . getLatestBlock ( ) ; if ( ! latestBlock . baseFeePerGas ) { throw new Error ( 'An error occurred while fetching current base fee, falling back' ) ; } const baseFee = bigify ( latestBlock . baseFeePerGas ) ; const baseFeeGwei = bigify ( fromWei ( baseFee , 'gwei' ) ) ; const estimatedPriorityFee = await estimatePriorityFee ( provider , baseFeeGwei , latestBlock . number ) ; if ( estimatedPriorityFee === null ) { throw new Error ( 'An error occurred while estimating priority fee, falling back' ) ; } const maxPriorityFeePerGas = BigNumber . max ( estimatedPriorityFee , DEFAULT_PRIORITY_FEE ) ; const multiplier = getBaseFeeMultiplier ( baseFeeGwei ) ; const potentialMaxFee = baseFee . multipliedBy ( multiplier ) ; const maxFeePerGas = maxPriorityFeePerGas . gt ( potentialMaxFee ) ? potentialMaxFee . plus ( maxPriorityFeePerGas ) : potentialMaxFee ; if ( toGwei ( maxFeePerGas ) . gte ( MAX_GAS_FAST ) || toGwei ( maxPriorityFeePerGas ) . gte ( MAX_GAS_FAST ) ) { throw new Error ( 'Estimated gas fee was much higher than expected, erroring' ) ; } return { maxFeePerGas : roundToWholeGwei ( maxFeePerGas ) , maxPriorityFeePerGas : roundToWholeGwei ( maxPriorityFeePerGas ) , baseFee } ; } catch ( err ) { console . error ( err ) ; return FALLBACK_ESTIMATE ; } } ;