3

I'm wondering how can I get the correct current average gas price ( or the new parameters maxPriorityFeePerGas and maxFeePerGas ) to push to my web3 wallet in order to have my transaction mined?

I know that there is an rpc call that you make that returns the current gasPrice of the rpc node you are calling, but it always seems not dynamic, by dynamic I mean that i'd like the value to change over time to match the real variation of gasPrice used by transactions of the latest blocks.

I mean, in a self hosted geth node, there is a way for retriving a dynamic value for this attributes? they seems like hardcoded and never get updated, for example from the GETH cli eth.gasPrice , eth.maxPriorityFeePerGas returns always 1000000000 why is that? do i have to implement myself a tracker that based on the latest 'x' blocks it returns the average gasPrice used or does geth have an internal logic that already does this that I can call ? Thanks in advance :)

2 Answers 2

3
+50

Let me try to elaborate on this. As you will not get real-time network activity with eth.gasPrice and eth.maxPriorityFeePerGas on a self-hosted geth node as geth does not actively monitor or compute the prices based on ongoing activity, you have the option to get gas price history from EIP-1559 on with eth_feeHistory. Otherwise, you may use a third-party provider.


Tracking Gas with eth_feeHistory

You may build a custom gas tracker script that helps you get the average gas price of the latest blocks. If you run a geth version that supports EIP-1559 the eth_feeHistory RPC method can help provide you dynamic fee estimates, because it returns you historical gas fee data, for the desired block range, up to 1024 blocks. You can check the Ethereum JSON-RPC playground to find more about eth_feeHistory.

https://ethereum-json-rpc.com/?method=eth_feeHistory

From the Ethereum JSON-RPC playground:

Returns transaction base fee per gas and effective priority fee per gas for the requested/supported block range.

example request:

{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": [
        "0x1",
        "latest",
        []
    ]
}

example response:

{
    "jsonrpc": "2.0",
    "result": {
        "oldestBlock": "0x141a1a5",
        "baseFeePerGas": [
            "0x205fcd8ce",
            "0x1f00f64eb"
        ],
        "gasUsedRatio": [
            0.3306712666666667
        ]
    }
}

lets look at an example implementation, here with js and python.

javascript example: https://docs.web3js.org/libdocs/Web3Eth/#getfeehistory

web3.eth.getFeeHistory(4, 'pending', [0, 25, 75, 100]).then(console.log);
> {
    baseFeePerGas: [
        22983878621n,
        21417903463n,
        19989260230n,
        17770954829n,
        18850641304n
    ],
    gasUsedRatio: [
        0.22746546666666667,
        0.2331871,
        0.05610054885262125,
        0.7430227268212117
    ],
    oldestBlock: 15216343n,
    reward: [
        [ '0x3b9aca00', '0x53724e00', '0x77359400', '0x1d92c03423' ],
        [ '0x3b9aca00', '0x3b9aca00', '0x3b9aca00', '0xee6b2800' ],
        [ '0x3b9aca00', '0x4f86a721', '0x77d9743a', '0x9502f900' ],
        [ '0xcc8ff9e', '0x53724e00', '0x77359400', '0x1ec9771bb3' ]
    ]
}

web3.eth.getFeeHistory(4, BlockTags.LATEST, [0, 25, 75, 100], { number: FMT_NUMBER.NUMBER , bytes: FMT_BYTES.HEX }).then(console.log);
> {
    baseFeePerGas: [
        22983878621,
        21417903463,
        19989260230,
        17770954829,
        18850641304
    ],
    gasUsedRatio: [
        0.22746546666666667,
        0.2331871,
        0.05610054885262125,
        0.7430227268212117
    ],
    oldestBlock: 15216343,
    reward: [
        [ '0x3b9aca00', '0x53724e00', '0x77359400', '0x1d92c03423' ],
        [ '0x3b9aca00', '0x3b9aca00', '0x3b9aca00', '0xee6b2800' ],
        [ '0x3b9aca00', '0x4f86a721', '0x77d9743a', '0x9502f900' ],
        [ '0xcc8ff9e', '0x53724e00', '0x77359400', '0x1ec9771bb3' ]
    ]
}

python example: https://web3py.readthedocs.io/en/stable/web3.eth.html#web3.eth.Eth.fee_history

>>> w3.eth.fee_history(4, 'latest', [10, 90])
AttributeDict({
    'oldestBlock': 3,
    'reward': [[220, 7145389], [1000000, 6000213], [550, 550], [125, 12345678]],
    'baseFeePerGas': [202583058, 177634473, 155594425, 136217133, 119442408],
    'gasUsedRatio': [0.007390479689642084, 0.0036988514889990873, 0.0018512333048507866, 0.00741217041320997]
})

Third-Party API

Just in case you do not want to build a custom tracker, or use eth_feeHistory, you may check external data providers, here you can find the endpoint on Infura API, or in the Alchemy API, to provide two examples.

1

Mat,

As far as I understand you don't need "gas price" (whatever that means) to create new EIP1559 transaction.

New dynamic transaction require 3 gas-related params: GasTipCap, GasFeeCap, Gas

tx := types.NewTx(&types.DynamicFeeTx{
    ChainID:   chainID,
    Nonce:     nonce,
    GasTipCap: gasParams.GasTipCap, // <- We need this
    GasFeeCap: gasParams.GasFeeCap, // <- and also this
    Gas:       gasParams.Gas,       // <- and finally this
    To:        to,
    Value:     value,
    Data:      data,
})

I made a function which requests/calculates these 3 params, and this approach works great for me:

Params we need (gathered in one structure for usability and consistency):

type Gas1559Params struct {
    GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas
    GasFeeCap *big.Int // a.k.a. maxFeePerGas
    Gas       uint64
}

And finally function, that returns these three params together:

const GAS_TIP_CAP = 2_000_000_000 // Hardcoded Tip = 2Gwei, make adjustable?

func GetGasParameters(ethClient *ethclient.Client, from common.Address, to *common.Address, value *big.Int, data []byte) (Gas1559Params, error) {
    header, err := ethClient.HeaderByNumber(context.Background(), nil)

    if err != nil {
        return Gas1559Params{}, fmt.Errorf("failed to request last block header: %s", err)
    }

    baseFee := header.BaseFee
    gasTipCap := big.NewInt(GAS_TIP_CAP)              // Calculate the gas tip cap (e.g., 2 Gwei)
    gasFeeCap := new(big.Int).Add(baseFee, gasTipCap) // Calculate the max fee per gas (baseFee + gasTipCap)

    gasLimit, err := estimateGas(ethClient, from, to, value, data)

    if err != nil {
        return Gas1559Params{}, err
    }

    return Gas1559Params{
        GasTipCap: gasTipCap,
        GasFeeCap: gasFeeCap,
        Gas:       gasLimit,
    }, nil
}

func estimateGas(ethClient *ethclient.Client, from common.Address, to *common.Address, value *big.Int, data []byte) (gasLimit uint64, err error) {
    msg := ethereum.CallMsg{
        From:  from,
        To:    to,
        Value: value,
        Data:  data,
    }

    gasLimit, err = ethClient.EstimateGas(context.Background(), msg)

    if err != nil {
        return 0, fmt.Errorf("failed to estimate gas limit for given operation: %s", err)
    }

    return gasLimit, nil
}

I hope this helps :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.