Skip to content

Commit

Permalink
feat: Add gas estimation utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Cory authored and Will Cory committed Jul 12, 2023
1 parent 6431ffb commit 06954fe
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
58 changes: 58 additions & 0 deletions packages/contracts-ts/src/estimateGas.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { vi, test, expect, beforeEach } from 'vitest'
import { baseFee, decimals, gasPrice, getL1Fee, getL1GasUsed, l1BaseFee, overhead, scalar, version } from './estimateGas'

// https://optimistic.etherscan.io/tx/0xceb34708f1de2ea2336dbdd81ca2cb7b12ec2ab6adc86a0d7d81c8c4416f81bb
const data = '0x5c19a95c00000000000000000000000046abfe1c972fca43766d6ad70e1c1df72f4bb4d1'

const chainConfig =
{
chainId: 10,
rpcUrl: process.env.VITE_L2_RPC_URL ?? 'https://mainnet.optimism.io',
} as const

const blockConfig = {
blockNumber: BigInt(106773236),
}

const params = { ...chainConfig, ...blockConfig };

beforeEach(() => {
vi.resetAllMocks()
})

test('baseFee should return the correct result', async () => {
expect(await baseFee(params)).toMatchInlineSnapshot('64n')
})

test('decimals should return the correct result', async () => {
expect(await decimals(params)).toMatchInlineSnapshot('6n')
})

test('gasPrice should return the correct result', async () => {
expect(await gasPrice(params)).toMatchInlineSnapshot('64n')
})

test('getL1Fee should return the correct result', async () => {
expect(await getL1Fee(data, params)).toMatchInlineSnapshot('15860541911298n')
})

test('getL1GasUsed should return the correct result', async () => {
expect(await getL1GasUsed(data, params)).toMatchInlineSnapshot('1708n')
})

test('l1BaseFee should return the correct result', async () => {
expect(await l1BaseFee(params)).toMatchInlineSnapshot('13576069538n')
})

test('overhead should return the correct result', async () => {
expect(await overhead(params)).toMatchInlineSnapshot('188n')
})

test('scalar should return the correct result', async () => {
expect(await scalar(params)).toMatchInlineSnapshot('684000n')
})

test('version should return the correct result', async () => {
expect(await version(params)).toMatchInlineSnapshot('"1.0.0"')
})

104 changes: 104 additions & 0 deletions packages/contracts-ts/src/estimateGas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { gasPriceOracleABI, gasPriceOracleAddress } from "./constants"
import { getContract, createPublicClient, http, BlockTag } from 'viem'
import * as chains from 'viem/chains'


type Bytes = `0x${string}`

// Generated addresses don't generate mainnet at this moment
type BlockOptions = {
blockNumber?: bigint | undefined
blockTag?: BlockTag | undefined
}

type ProviderOptions = {
chainId: keyof typeof gasPriceOracleAddress
rpcUrl?: string | undefined
nativeCurrency?: chains.Chain['nativeCurrency']
} | {
chainId: number
rpcUrl: string
nativeCurrency?: chains.Chain['nativeCurrency']
}

const warnAboutFetch = () => {
if (typeof fetch === 'undefined') {
console.error('No fetch implementation found. Please provide a fetch polyfill. This can be done in NODE by passing in NODE_OPTIONS=--experimental-fetch or by using the isomorphic-fetch npm package')
}
}

const getL2Client = ({ chainId, rpcUrl, nativeCurrency }: ProviderOptions) => {
warnAboutFetch()
const viemChain = Object.values(chains)?.find(chain => chain.id === chainId)
const rpcUrls = rpcUrl ? { default: { http: [rpcUrl] }, public: { http: [rpcUrl] } } : viemChain?.rpcUrls
if (!rpcUrls) {
throw new Error(`No rpcUrls found for chainId ${chainId}. Please explicitly provide one`)
}
return createPublicClient({
chain: {
id: chainId,
name: viemChain?.name ?? 'op-chain',
nativeCurrency: nativeCurrency ?? viemChain?.nativeCurrency ?? chains.optimism.nativeCurrency,
network: viemChain?.network ?? 'Unknown OP Chain',
rpcUrls,
explorers: (viemChain as typeof chains.optimism)?.blockExplorers ?? chains.optimism.blockExplorers,
},
transport: http(rpcUrl ?? chains[chainId].rpcUrls.public.http[0]),
})
}

const getGasPriceOracleContract = (params: ProviderOptions) => {
return getContract({
address: gasPriceOracleAddress['420'],
abi: gasPriceOracleABI,
publicClient: getL2Client(params)
})
}

export type GasPriceOracleOptions = BlockOptions & ProviderOptions

export const baseFee = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.baseFee(params)
}

export const decimals = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.decimals(params)
}

export const gasPrice = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.gasPrice(params)
}

export const getL1Fee = (data: Bytes, params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.getL1Fee([data], params)
}

export const getL1GasUsed = (data: Bytes, params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.getL1GasUsed([data], params)
}

export const l1BaseFee = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.l1BaseFee(params)
}

export const overhead = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.overhead(params)
}

export const scalar = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.scalar(params)
}

export const version = (params: GasPriceOracleOptions) => {
const contract = getGasPriceOracleContract(params)
return contract.read.version(params)
}

0 comments on commit 06954fe

Please sign in to comment.