Skip to content

Commit

Permalink
fix: minimum fee of 0.1 gwei on optimism
Browse files Browse the repository at this point in the history
  • Loading branch information
schmanu committed Aug 8, 2023
1 parent 425cf08 commit 14f807e
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 30 deletions.
95 changes: 70 additions & 25 deletions src/hooks/__tests__/useGasPrice.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { BigNumber } from 'ethers'
import { act, renderHook } from '@/tests/test-utils'
import useGasPrice from '@/hooks/useGasPrice'
import { useCurrentChain } from '../useChains'

// mock useWeb3Readonly
jest.mock('../wallets/web3', () => {
const provider = {
getFeeData: jest.fn(() =>
Promise.resolve({
gasPrice: undefined,
maxFeePerGas: BigNumber.from('0x956e'),
maxPriorityFeePerGas: BigNumber.from('0x136f'),
maxFeePerGas: BigNumber.from('0x956e'), //38254
maxPriorityFeePerGas: BigNumber.from('0x136f'), //4975
}),
),
}
Expand All @@ -19,30 +20,29 @@ jest.mock('../wallets/web3', () => {
})

// Mock useCurrentChain
const currentChain = {
chainId: '4',
gasPrice: [
{
type: 'ORACLE',
uri: 'https://api.etherscan.io/api?module=gastracker&action=gasoracle',
gasParameter: 'FastGasPrice',
gweiFactor: '1000000000.000000000',
},
{
type: 'ORACLE',
uri: 'https://ethgasstation.info/json/ethgasAPI.json',
gasParameter: 'fast',
gweiFactor: '200000000.000000000',
},
{
type: 'FIXED',
weiValue: '24000000000',
},
],
features: ['EIP1559'],
}
jest.mock('@/hooks/useChains', () => {
const currentChain = {
chainId: '4',
gasPrice: [
{
type: 'ORACLE',
uri: 'https://api.etherscan.io/api?module=gastracker&action=gasoracle',
gasParameter: 'FastGasPrice',
gweiFactor: '1000000000.000000000',
},
{
type: 'ORACLE',
uri: 'https://ethgasstation.info/json/ethgasAPI.json',
gasParameter: 'fast',
gweiFactor: '200000000.000000000',
},
{
type: 'FIXED',
weiValue: '24000000000',
},
],
features: ['EIP1559'],
}

return {
useCurrentChain: jest.fn(() => currentChain),
}
Expand All @@ -52,6 +52,7 @@ describe('useGasPrice', () => {
beforeEach(() => {
jest.useFakeTimers()
jest.clearAllMocks()
;(useCurrentChain as jest.Mock).mockReturnValue(currentChain)
})

it('should return the fetched gas price from the first oracle', async () => {
Expand Down Expand Up @@ -170,6 +171,50 @@ describe('useGasPrice', () => {
expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toEqual('4975')
})

it("should use the previous block's fee data if there are no oracles", async () => {
;(useCurrentChain as jest.Mock).mockReturnValue({
chainId: '1',
gasPrice: [],
features: ['EIP1559'],
})

const { result } = renderHook(() => useGasPrice())

await act(async () => {
await Promise.resolve()
})
// assert the hook is not loading
expect(result.current[2]).toBe(false)

// assert gas price from provider
expect(result.current[0]?.maxFeePerGas?.toString()).toBe('38254')

// assert priority fee from provider
expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toBe('4975')
})

it('should use a minimum max fee per gas of 0.1 gwei on optimism', async () => {
;(useCurrentChain as jest.Mock).mockReturnValue({
chainId: '10',
gasPrice: [],
features: ['EIP1559'],
})

const { result } = renderHook(() => useGasPrice())

await act(async () => {
await Promise.resolve()
})
// assert the hook is not loading
expect(result.current[2]).toBe(false)

// assert gas price as minimum of 0.1 gwei
expect(result.current[0]?.maxFeePerGas?.toString()).toBe('100000000')

// assert priority fee from provider
expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toBe('4975')
})

it('should keep the previous gas price if the hook re-renders', async () => {
// Mock fetch
Object.defineProperty(window, 'fetch', {
Expand Down
15 changes: 10 additions & 5 deletions src/hooks/useGasPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { useWeb3ReadOnly } from '../hooks/wallets/web3'
import { Errors, logError } from '@/services/exceptions'
import { FEATURES, hasFeature } from '@/utils/chains'
import { asError } from '@/services/exceptions/utils'
import { adjustGasEstimationForChain } from '@/utils/gas'

export type EstimatedGasPrice = {
maxFeePerGas: BigNumber | undefined
maxPriorityFeePerGas: BigNumber | undefined
}

// Update gas fees every 20 seconds
const REFRESH_DELAY = 20e3
Expand Down Expand Up @@ -53,10 +59,7 @@ const getGasPrice = async (gasPriceConfigs: GasPrice): Promise<BigNumber | undef
}
}

const useGasPrice = (): AsyncResult<{
maxFeePerGas: BigNumber | undefined
maxPriorityFeePerGas: BigNumber | undefined
}> => {
const useGasPrice = (): AsyncResult<EstimatedGasPrice> => {
const chain = useCurrentChain()
const gasPriceConfigs = chain?.gasPrice
const [counter] = useIntervalCounter(REFRESH_DELAY)
Expand All @@ -77,10 +80,12 @@ const useGasPrice = (): AsyncResult<{
const maxFee = gasPrice || (isEIP1559 ? feeData?.maxFeePerGas : feeData?.gasPrice) || undefined
const maxPrioFee = (isEIP1559 && feeData?.maxPriorityFeePerGas) || undefined

return {
const gasEstimation = {
maxFeePerGas: maxFee,
maxPriorityFeePerGas: maxPrioFee,
}

return adjustGasEstimationForChain(gasEstimation, chain?.chainId)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[gasPriceConfigs, provider, counter, isEIP1559],
Expand Down
19 changes: 19 additions & 0 deletions src/utils/gas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import chains from '@/config/chains'
import type { EstimatedGasPrice } from '@/hooks/useGasPrice'
import { BigNumber } from 'ethers'

const OPTIMISM_MINIMUM_BASE_FEE_WEI = BigNumber.from(100000000)

export const adjustGasEstimationForChain = (gas: EstimatedGasPrice, chainId: string | undefined) => {
if (chainId === chains.oeth) {
// On Optimism we lower bound the base fee to 0.1 gwei because the base fee can spike quite fast
return {
...gas,
maxFeePerGas: gas.maxFeePerGas?.lt(OPTIMISM_MINIMUM_BASE_FEE_WEI)
? OPTIMISM_MINIMUM_BASE_FEE_WEI
: gas.maxFeePerGas,
}
}

return gas
}

0 comments on commit 14f807e

Please sign in to comment.