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 exerted by the worker. 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 exerted. 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 the amount of 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, then after the job is completed, the excess 20 units will be refunded. In the context of the Ethereum network, this refers to how much Gas
you are willing to pay for this transaction at most.
There is a transaction as shown in the following transaction:
The Gas
section consists of the following parts:
Gas Limit & Usage by Txn
:GasLimit
and the actualGas
consumed, along with its proportion inGasLimit
Gas Fees
Base
: BaseGasPrice
Max
: MaximumGasPrice
Max Priority
:GasPrice
paid to Ethereum node miners
Burnt & Txn Savings Fees
Burnt
: Burned transaction feeTxn 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, returnInitialBaseFee
, which is1 Gwei
. -
Calculate
parentGasTarget
as half of the parent block'sGasLimit
.- If the parent block's
GasUsed
equalsparentGasTarget
:BaseFee
remains unchanged. - If the parent block's
GasUsed
is greater thanparentGasTarget
:BaseFee
increases, calculated according to the formula.
- If the parent block's
parentBaseFee + max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
- If the parent block's
GasUsed
is less thanparentGasTarget
:BaseFee
decreases, calculated according to the formula.
max(0, parentBaseFee - parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
Where:
GasUsed
: ActualGas
usedparentBaseFee
: Equals the parent block'sBaseFee
gasUsedDelta
: Equalsparent.GasUsed - parentGasTarget
, which is the difference between the actualGas
used by the parent block and the target totalparentGasTarget
: Half of the parent block'sGasLimit
, generally 15000000BaseFeeChangeDenominator
: Constant, valued at 8
On the etherscan
block list page, there are the following two blocks:
In block 18247918, it can be seen that
BaseFee
equals 6.79 GweigasUsedDelta / parentGasTarget
equals -24%, indicating thatBaseFee
needs to be reduced.
According to the formula, the BaseFee
for block 18247919 equals
This is consistent with the illustration.
MaxPriorityFee#
MaxPriorityFee
is the priority fee. It is an additional charge per unit of Gas
, and this portion of the fee will be paid to miners; the higher the 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:
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 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, the price per unit of labor (BaseFee
) is variable and determined by the market. After you post a job and also offer an extra reward per unit of labor (MaxPriorityFee
), if the reward you are willing to pay is below the market price, no one will be willing to work for you, and your 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. The price per unit of labor is still calculated as BaseFee + MaxPriorityFee
.
Typically, the calculation for MaxFee
follows the formula:
This ensures that even in the case of consecutive 6 blocks being full of Gas
, the transaction remains in the memory pool waiting to be packaged.
Burnt#
The burned transaction fee means that this portion of the fee is transferred to a black hole address. The amount transferred is determined by BaseFee
.
Taking the transaction in the illustration as an example:
This matches the value in the Burnt
field in the illustration.
Txn Savings#
The transaction savings fee equals the maximum acceptable transaction fee minus the actual transaction fee consumed.
Taking the transaction in the illustration as an example:
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 BaseFee
and other information.
// 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 previously obtained MaxPriorityFee
, it can be used to set MaxFee
.
Max Fee = (2 * BaseFee) + MaxPriorityFee