moon

moon

Build for builders on blockchain
github
twitter

Detailed Explanation of Ethereum Gas Mechanism (EIP-1559)

Gas in Ethereum is a unit of measurement required to execute smart contracts and conduct transactions. It represents the computational resources needed to perform operations on the network. Gas is used to prevent network abuse and ensure the efficiency of transaction and smart contract execution. Each operation and transaction has a Gas consumption, and users pay for it by setting a Gas price. The Gas mechanism helps maintain the healthy operation of the Ethereum network by regulating network load through resource consumption billing.

If we compare the Ethereum network to a worker, then Gas is the labor the worker expends. After the worker completes the job, they need to be compensated. The compensation equals the price per unit of labor multiplied by the total labor expended. The price per unit of labor is called GasPrice, and its value is dynamically determined by the Ethereum network. Therefore, the total compensation is Gas * GasPrice.

GasLimit can be understood as how much labor you are willing to pay for. If a job requires 100 units of labor, but you are only willing to pay for 80 units, then the job cannot be completed. However, if you are willing to pay for 120 units of labor, then after the job is completed, the excess 20 units will be refunded. In the context of the Ethereum network, it indicates how much Gas you are willing to pay for this transaction at most.

There is a transaction as shown in the following transaction:

transaction

The Gas section consists of the following parts:

  • Gas Limit & Usage by Txn: GasLimit and the actual Gas consumed, along with its proportion in GasLimit
  • Gas Fees
    • Base: Base GasPrice
    • Max: Maximum GasPrice
    • Max Priority: GasPrice paid to Ethereum node miners
  • Burnt & Txn Savings Fees
    • Burnt: Burned transaction fee
    • Txn Savings: Fees saved from the transaction

Txn Type: 2(EIP-1559): According to EIP-2718, the transaction type is explicitly defined as 2, indicating that this is an EIP-1559 transaction.

BaseFee#

BaseFee is a mechanism introduced in the EIP-1559 proposal, aimed at improving Ethereum's fee market and enhancing user experience. BaseFee is the base fee for each block, designed to reflect the level of network congestion by adaptively adjusting fees.

The calculation of BaseFee in the source code is as follows:

func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
	// If the current block is the first EIP-1559 block, return the InitialBaseFee.
	if !config.IsLondon(parent.Number) {
		return new(big.Int).SetUint64(params.InitialBaseFee)
	}

	parentGasTarget := parent.GasLimit / config.ElasticityMultiplier()
	// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
	if parent.GasUsed == parentGasTarget {
		return new(big.Int).Set(parent.BaseFee)
	}

	var (
		num   = new(big.Int)
		denom = new(big.Int)
	)

	if parent.GasUsed > parentGasTarget {
		// If the parent block used more gas than its target, the baseFee should increase.
		// max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
		num.SetUint64(parent.GasUsed - parentGasTarget)
		num.Mul(num, parent.BaseFee)
		num.Div(num, denom.SetUint64(parentGasTarget))
		num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
		baseFeeDelta := math.BigMax(num, common.Big1)

		return num.Add(parent.BaseFee, baseFeeDelta)
	} else {
		// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
		// max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
		num.SetUint64(parentGasTarget - parent.GasUsed)
		num.Mul(num, parent.BaseFee)
		num.Div(num, denom.SetUint64(parentGasTarget))
		num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
		baseFee := num.Sub(parent.BaseFee, num)

		return math.BigMax(baseFee, common.Big0)
	}
}

The process of calculating BaseFee is as follows:

  • If the current block is the first EIP-1559 block, return InitialBaseFee, which is 1 Gwei.

  • Calculate parentGasTarget as half of the parent block's GasLimit.

    • If the parent block's GasUsed equals parentGasTarget: BaseFee remains unchanged.
    • If the parent block's GasUsed is greater than parentGasTarget: BaseFee increases, calculated according to the formula.
parentBaseFee + max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
  • If the parent block's GasUsed is less than parentGasTarget: BaseFee decreases, calculated according to the formula.
max(0, parentBaseFee - parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)

Where:

  • GasUsed: The actual Gas used.
  • parentBaseFee equals the parent block's BaseFee.
  • gasUsedDelta equals parent.GasUsed - parentGasTarget, which is the difference between the actual Gas used by the parent block and the target total.
  • parentGasTarget is half of the parent block's GasLimit, generally around 15000000.
  • BaseFeeChangeDenominator is a constant with a value of 8.

On the etherscan block list page, there are the following two blocks:

blocks

In block 18247918, it can be seen that:

  • BaseFee equals 6.79 Gwei.
  • gasUsedDelta / parentGasTarget equals -24%, indicating that BaseFee needs to be lowered.

According to the formula, the BaseFee for block 18247919 equals

BaseFee=max(0,6.796.790.24/8)=6.5863\mathrm{BaseFee = max(0, 6.79 - 6.79 * 0.24 / 8) = 6.5863}

This is consistent with the illustration.

MaxPriorityFee#

MaxPriorityFee is the priority fee. It is an additional markup for each unit of Gas, and this portion of the fee will be paid to miners. The larger this value, the faster the transaction will be packaged. The current latest Gas information can be viewed through GasTracker.

The final GasPrice equals the sum of BaseFee and MaxPriorityFee.

For the transaction:

transaction

Transaction Fee=GasUsedGasPrice=GasUsed(BaseFee+MaxPriorityFee)=115855(7.407585749+0.05)=863998.597Gwei\begin{aligned} \tt{Transaction\ Fee} & = \tt{GasUsed * GasPrice} \\ &= \tt{GasUsed * (BaseFee + MaxPriorityFee)} \\ &= 115855 * (7.407585749 + 0.05) \\ & = 863998.597\tt{Gwei} \end{aligned}

The transaction fee matches the value in the Transaction Fee field in the illustration.

MaxFee#

MaxFee refers to the maximum GasPrice.

Since the sent transaction may not necessarily be packaged in the next block, and BaseFee is dynamically changing, if the MaxPriorityFee set for the transaction is too low, the transaction may not be packaged. You can only wait for subsequent blocks to be packaged. However, if the BaseFee of the subsequent blocks is higher than before, it may cause the transaction to be discarded. Setting a higher MaxFee can ensure that the transaction will not be discarded in the next few blocks due to a low BaseFee.

Using labor as an example again, now the price per unit of labor (BaseFee) is variable and determined by the market. After you post a job, if you are willing to pay an additional reward (MaxPriorityFee) per unit of labor, but the reward you are willing to pay is lower than the market price, then no one will be willing to work for you, and your posted job will be taken down. Therefore, you provide the maximum price per unit of labor (MaxFee), as long as the current BaseFee plus MaxPriorityFee is less than MaxFee, you can continue to hire workers. The price per unit of labor is still calculated as BaseFee + MaxPriorityFee.

Typically, the calculation for MaxFee follows the formula:

MaxFee=(2BaseFee)+MaxPriorityFee\tt{Max Fee = (2 * BaseFee) + MaxPriorityFee}

This ensures that even in the case of six consecutive blocks being full of Gas, the transaction remains in the memory pool waiting to be packaged.

Burnt#

The burned transaction fee refers to transferring this portion of the fee to a black hole address. The amount transferred is determined by BaseFee.

Burnt=BaseFeeGasUsed\mathtt{Burnt = BaseFee * GasUsed}

Taking the transaction in the illustration as an example:

Burnt=BaseFeeGasUsed=7.407585749115855=858205.846950395 Gwei\begin{aligned} \tt{Burnt} & = \tt{BaseFee * GasUsed} \\ & = 7.407585749 * 115855 \\ &= 858205.846950395 \ \tt{Gwei} \end{aligned}

This matches the value in the Burnt field in the illustration.

Txn Savings#

Transaction savings fees equal the maximum acceptable transaction fee minus the actual transaction fee consumed.

TxSavingsFees=MaxFeeGasUsed(BaseFee+MaxPriorityFee)GasUsed\tt{Tx Savings Fees = MaxFee * GasUsed - (BaseFee + MaxPriorityFee) * GasUsed}

Taking the transaction in the illustration as an example:

TxSavingsFees=MaxFeeGasUsed(BaseFee+MaxPriorityFee)GasUsed=7.657591636115855863998.597=23171.68198878Gwei\begin{aligned} \tt{Tx Savings Fees} & = \tt{MaxFee * GasUsed - (BaseFee + MaxPriorityFee) * GasUsed} \\ & = 7.657591636 * 115855 - 863998.597 \\ & = 23171.68198878 \tt{Gwei} \\ \end{aligned}

This matches the value in the Txn Savings field in the illustration.

JSON-RPC#

When initiating an EIP1559 transaction, it is often necessary to manually fill in Gas related parameters in the transaction. These parameters can be obtained by sending http requests to the node, including the following JSON-RPC methods:

  • eth_estimateGas
  • eth_maxPriorityFeePerGas
  • eth_getBlockByNumber

eth_estimateGas#

Sending a transaction to this interface can provide an estimated Gas, commonly used to set the transaction's GasLimit.

// Request Payload
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [
    {
      "from": "0xD28C383dd3a1C0154129F67067175884e933cf4e",
      "to": "0x7071D6EF9FaF45aA48c22bae7d4a295aD68DC038",
      "value": "0x186a0"
    }
  ],
  "id": 1
}
// Response
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0x5208" // 21000
}

eth_maxPriorityFeePerGas#

This interface is used to obtain the current latest MaxPriorityFee.

// Request Payload
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
// Response
{
  "jsonrpc": "2.0",
  "result": "0x9b8495", // MaxPriorityFee
  "id": 1
}

eth_getBlockByNumber#

This interface is used to obtain block information, which includes information such as BaseFee.

// Request Payload
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": [
    "latest",
    false
  ],
  "id": 1
}
// Response
{
  "jsonrpc": "2.0",
  "result": {
    "baseFeePerGas": "0x1bc47470a", // baseFee
    "difficulty": "0x0",
    "extraData": "0x546974616e2028746974616e6275696c6465722e78797a29",
    "gasLimit": "0x1c9c380",
    "gasUsed": "0xced6fd",
    "hash": "0xbb9b314d0b8208e655a0afc17384f56f44659a63e3ba4e244609105da497a7d9",
    ...
  },
  "id": 1
}

After obtaining the latest block information, the value of the field baseFeePerGas is the BaseFee. Combined with the MaxPriorityFee obtained above, it can be used to set the MaxFee.

Max Fee = (2 * BaseFee) + MaxPriorityFee
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.