Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get EIP 1559 gas fees for linea #313

Merged
merged 2 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ethcode",
"displayName": "ETHcode",
"description": "Ethereum IDE for VS Code",
"version": "0.4.9",
"version": "0.4.11",
"publisher": "7finney",
"categories": [
"Debuggers",
Expand All @@ -19,7 +19,7 @@
"icon": "images/ethcode.png",
"repository": "https://github.com/7finney/ethcode",
"engines": {
"vscode": "^1.80.0",
"vscode": "^1.84.2",
"node": ">=16.15.0"
},
"activationEvents": [
Expand Down Expand Up @@ -47,13 +47,12 @@
},
"markdownDescription": "List of networks settings including ethereum, polygon etc",
"default": {
"Ethereum": "{\"rpc\": \"homestead\",\"blockScanner\":\"https://etherscan.io\", \"chainID\": \"1\", \"nativeCurrency\": {\"name\": \"Ether\",\"symbol\":\"ETH\",\"decimals\": \"18\"}}",
"Ethereum": "{\"rpc\": \"https://rpc.ankr.com/eth\",\"blockScanner\":\"https://etherscan.io\", \"chainID\": \"1\", \"nativeCurrency\": {\"name\": \"Ether\",\"symbol\":\"ETH\",\"decimals\": \"18\"}}",
"Ganache Testnet": "{\"rpc\": \"http://127.0.0.1:7545\", \"blockScanner\": \"https://etherscan.io\", \"chainID\": \"1337\", \"nativeCurrency\": {\"name\": \"Ganache Ether\",\"symbol\":\"ETH\",\"decimals\": \"18\"}}",
"Hardhat Testnet": "{\"rpc\": \"http://127.0.0.1:8545\", \"blockScanner\": \"https://etherscan.io\", \"chainID\": \"1337\", \"nativeCurrency\": {\"name\": \"Hardhat Ether\",\"symbol\":\"ETH\",\"decimals\": \"18\"}}",
"Goerli Testnet": "{\"rpc\": \"goerli\", \"blockScanner\": \"https://goerli.etherscan.io\", \"chainID\": \"5\", \"nativeCurrency\": {\"name\": \"Görli Ether\",\"symbol\":\"görETH\",\"decimals\": \"18\"}}",
"Polygon": "{\"rpc\": \"https://polygon-rpc.com\", \"blockScanner\": \"https://polygonscan.com\", \"chainID\": \"137\", \"nativeCurrency\": {\"name\": \"Polygon Matic\",\"symbol\":\"MATIC\",\"decimals\": \"18\"}}",
"Polygon Mainnet": "{\"rpc\": \"https://polygon-rpc.com\", \"blockScanner\": \"https://polygonscan.com\", \"chainID\": \"137\", \"nativeCurrency\": {\"name\": \"Polygon Matic\",\"symbol\":\"MATIC\",\"decimals\": \"18\"}}",
"Sepolia Testnet": "{\"rpc\": \"https://rpc.sepolia.dev\", \"blockScanner\": \"https://sepolia.etherscan.io\",\"chainID\": \"11155111\", \"nativeCurrency\": {\"name\": \"Sepolia Ether\",\"symbol\":\"ETH\",\"decimals\": \"18\"}}",
"Polygon Mumbai": "{\"rpc\": \"https://rpc-mumbai.maticvigil.com\", \"blockScanner\": \"https://mumbai.polygonscan.com\",\"chainID\": \"80001\", \"nativeCurrency\": {\"name\": \"Polygon Mumbai Matic\",\"symbol\":\"mMATIC\",\"decimals\": \"18\"}}"
"Polygon Amoy": "{\"rpc\": \"https://rpc.ankr.com/polygon_amoy\", \"blockScanner\": \"https://amoy.polygonscan.com\",\"chainID\": \"80002\", \"nativeCurrency\": {\"name\": \"Matic\",\"symbol\":\"MATIC\",\"decimals\": \"18\"}}"
}
},
"ethcode.gasLimit": {
Expand Down Expand Up @@ -115,6 +114,11 @@
"title": "Set transaction gas strategy",
"category": "Ethcode"
},
{
"command": "ethcode.transaction.gas.prices",
"title": "Get network gas prices",
"category": "Ethcode"
},
{
"command": "ethcode.compiled-json.load",
"title": "Ethcode: Load compiled JSON output"
Expand Down Expand Up @@ -198,7 +202,7 @@
"devDependencies": {
"@types/node": "^18.0.0",
"@types/utf8": "^3.0.1",
"@types/vscode": "^1.38.1",
"@types/vscode": "^1.84.2",
"@typescript-eslint/eslint-plugin": "^5.52.0",
"@typescript-eslint/parser": "^5.52.0",
"eslint-config-standard-with-typescript": "^34.0.0",
Expand Down
10 changes: 8 additions & 2 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {
getSelectedNetConf,
getSelectedProvider
} from '../utils/networks'
import { type ContractABI, type CompiledJSONOutput, type NetworkConfig } from '../types'
import { type ContractABI, type CompiledJSONOutput, type NetworkConfig, type Fees } from '../types'
import {
createConstructorInput,
createDeployed,
createFunctionInput,
getConstructorInputFullPath,
getDeployedFullPath,
getFunctionInputFullPath
getFunctionInputFullPath,
getGasPrices
} from '../utils/functions'
import { type JsonFragment } from '@ethersproject/abi'
import { logger } from '../lib'
Expand Down Expand Up @@ -197,6 +198,10 @@ const createContractFiles = async (context: vscode.ExtensionContext, contractTit

logger.success(`Contract ${name[0]} is selected.`)
}

const getNetworkGasPrices = async (context: vscode.ExtensionContext): Promise<Fees> => {
return await getGasPrices(context)
}
export {
getNetwork,
setNetwork,
Expand All @@ -211,5 +216,6 @@ export {
getFunctionInputFile,
getConstructorInputFile,
createContractFiles,
getNetworkGasPrices,
event
}
22 changes: 19 additions & 3 deletions src/api/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import {
getNetwork,
providerDefault,
setNetwork,
getAvailableNetwork
getAvailableNetwork,
getNetworkGasPrices
} from './api'
import {
type ExtensionContext
} from 'vscode'
import { type Provider } from '@ethersproject/providers'
import { type NetworkConfig } from '../types'
import { type Fees, type NetworkConfig } from '../types'

/**
* Interface for the provider API.
Expand Down Expand Up @@ -47,6 +48,12 @@ export interface ProviderInterface {
* @returns An array of the names of all available networks.
*/
list: () => string[]

/**
* Returns the current gas price of the network selected in the extension.
* @returns the current gas price of the network selected in the extension.
*/
getGasPrices: () => Promise<any>
}
}

Expand Down Expand Up @@ -95,12 +102,21 @@ export function provider (context: ExtensionContext): ProviderInterface {
return getAvailableNetwork()
};

/**
* Returns the current gas price of the network selected in the extension.
* @returns the current gas price of the network selected in the extension.
*/
async function getGasPrices (): Promise<Fees> {
return await getNetworkGasPrices(context)
}

return {
get,
network: {
get: networkGet,
set: networkSet,
list: networkList
list: networkList,
getGasPrices
}
}
}
7 changes: 7 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ export async function activate (context: ExtensionContext): Promise<API | undefi
})
}),

// Get network gas prices
commands.registerCommand('ethcode.transaction.gas.prices', async () => {
const { maxFeePerGas, maxPriorityFeePerGas } = await provider(context).network.getGasPrices()
logger.log('maxFeePerGas > ', maxFeePerGas)
logger.log('maxPriorityFeePerGas > ', maxPriorityFeePerGas)
}),

// Load combined JSON output
commands.registerCommand('ethcode.compiled-json.load', () => {
const editorContent = (window.activeTextEditor != null)
Expand Down
12 changes: 12 additions & 0 deletions src/types/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,15 @@ export interface BytecodeObject {
/** If given, this is an unlinked object. */
linkReferences?: Record<string, Record<string, Array<{ start: number, length: number }>>>
}

export interface Fees {
maxFeePerGas: bigint
maxPriorityFeePerGas?: bigint
}

export interface FeeHistory {
oldestBlock: number
reward: string[][]
baseFeePerGas: string[]
gasUsedRatio: number[]
}
104 changes: 60 additions & 44 deletions src/utils/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
type ConstructorInputValue,
getAbi,
type IFunctionQP,
type EstimateGas
type EstimateGas,
type Fees
} from '../types'
import { logger } from '../lib'
import { errors } from '../config'
Expand All @@ -19,8 +20,10 @@ import {
writeFunction
} from '../lib/file'
import { getSelectedNetConf } from './networks'

import { get1559Fees } from './get1559Fees'
import { getSelectedProvider } from './utils'
import axios from 'axios'
import { type ethers } from 'ethers'

const createDeployed: any = (contract: CompiledJSONOutput) => {
const fullPath = getDeployedFullPath(contract)
Expand Down Expand Up @@ -98,11 +101,6 @@ const createFunctionInput: any = (contract: CompiledJSONOutput) => {
}))]
})
}))
// const functions = functionsAbi.map((e: { name: any, stateMutability: any, inputs: any[] }) => ({
// name: e.name,
// stateMutability: e.stateMutability,
// inputs: e.inputs?.map((c) => ({ ...c, value: '' }))
// }))
console.log(functions)

writeFunction(getFunctionInputFullPath(contract), contract, functions)
Expand Down Expand Up @@ -265,50 +263,68 @@ const getNetworkBlockpriceUrl: any = (context: vscode.ExtensionContext) => {
} else { /* empty */ }
}

export const getGasPrices = async (context: vscode.ExtensionContext): Promise<Fees> => {
const provider = getSelectedProvider(context) as ethers.providers.JsonRpcProvider
const feeData = await provider.getFeeData()
return {
maxFeePerGas: feeData.maxFeePerGas?.toBigInt() ?? BigInt(0),
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.toBigInt() ?? BigInt(0)
}
}

const getGasEstimates: any = async (
condition: string,
context: vscode.ExtensionContext
) => {
let estimate: EstimateGas | undefined
const blockPriceUri = getNetworkBlockpriceUrl(context)
if (blockPriceUri !== undefined) {
await axios
.get(blockPriceUri, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers':
'Origin, X-Requested-With, Content-Type, Accept'
}
})
.then((res: any) => {
if (res.status === 200) {
switch (condition) {
case 'Low': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 70
) as EstimateGas
break
}
case 'Medium': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 90
) as EstimateGas
break
}
case 'High': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 99
) as EstimateGas
break
const chainID = getSelectedNetConf(context).chainID
// try to use `eth_feeHistory` RPC API
const provider = getSelectedProvider(
context
) as ethers.providers.JsonRpcProvider
if (chainID === '59140') {
const maxFeePerGas = get1559Fees(provider, BigInt(10), 70)
console.log(maxFeePerGas)
} else {
const blockPriceUri = getNetworkBlockpriceUrl(context)
if (blockPriceUri !== undefined) {
await axios
.get(blockPriceUri, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers':
'Origin, X-Requested-With, Content-Type, Accept'
}
})
.then((res: any) => {
if (res.status === 200) {
switch (condition) {
case 'Low': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 70
) as EstimateGas
break
}
case 'Medium': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 90
) as EstimateGas
break
}
case 'High': {
estimate = res.data.blockPrices[0].estimatedPrices.find(
(x: any) => x.confidence === 99
) as EstimateGas
break
}
}
return estimate
}

return estimate
}
})
.catch((error: any) => {
console.error(error)
})
})
.catch((error: any) => {
console.error(error)
})
}
}

return estimate
Expand Down
31 changes: 31 additions & 0 deletions src/utils/get1559Fees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { type ethers } from 'ethers'
import { type Fees, type FeeHistory } from '../types'

export async function get1559Fees (
provider: ethers.providers.JsonRpcProvider,
maxFeePerGasFromConfig: bigint,
percentile: number
): Promise<Fees> {
const { reward, baseFeePerGas }: FeeHistory = await provider.send('eth_feeHistory', ['0x5', 'latest', [percentile]])

const maxPriorityFeePerGas = reward.reduce((accumulator, currentValue) => BigInt(accumulator) + BigInt(currentValue[0]), BigInt(0)) / BigInt(reward.length)

if (maxPriorityFeePerGas > 0 && maxPriorityFeePerGas > maxFeePerGasFromConfig) {
throw new Error(
`Estimated miner tip of ${maxPriorityFeePerGas} exceeds configured max fee per gas of ${maxFeePerGasFromConfig}.`
)
}

const maxFeePerGas = BigInt(baseFeePerGas[baseFeePerGas.length - 1]) * BigInt(2) + BigInt(maxPriorityFeePerGas)

if (maxFeePerGas > 0 && maxPriorityFeePerGas > 0) {
return {
maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas),
maxFeePerGas: maxFeePerGas > maxFeePerGasFromConfig ? maxFeePerGasFromConfig : maxFeePerGas
}
}

return {
maxFeePerGas: maxFeePerGasFromConfig
}
}
Loading