{"author":{"address":"0x2b754dEF498d4B6ADada538F01727Ddf67D91A7D","user":"https://learnblockchain.cn/people/20334"},"content":{"body":"## 基础概念：以太坊中的Gas是什么？\r\n\r\n以太坊被认为是一个世界计算机，它的运行需要耗费资源，为了确保网络不会被滥用和因错误的使用陷入宕机，任何交易的操作都需要支付一定的费用，这个费用我们可以简单称之为\"Gas\"。它代表了网络中执行操作所需的计算资源。\r\n\r\n做个简单的类比可以帮助我们更好的理解，如果把以太坊网络比作一名工人，那么 `Gas` 就是工人付出的劳动力。在工人完成工作后，需要支付劳动报酬。劳动报酬则等于**每单位劳动力价格**乘以**付出的总的劳动力**。每单位劳动力价格被称作 `GasPrice`, 其值由以太坊网络动态决定的。因此总的劳动报酬就是 `Gas * GasPrice`\r\n\r\n`GasLimit` 可以理解为愿意为多少劳动力买单。假如某项工作需要付出100个单位的劳动力，但你只愿意支付80个单位劳动力的费用。这项工作就无法完成。但当你愿意支付120个单位劳动力的费用时，则会在工作完成后会退还这20个单位的劳动力费用。类比到以太坊网络就是愿意为这笔交易最多支付多少 `Gas`。\r\n\r\n## 一个实际的交易展示\r\n\r\n下图展示了一笔真实的[交易](https://etherscan.io/tx/0x2a47d57a8bfd4b8fbfbbad922509b8bed45c470c73068de3623388c6d3aaf117) \r\n\r\n\r\n![Xnip2024-07-03_18-54-31.png](https://img.learnblockchain.cn/attachments/2024/07/627BkWLk6685f6ff9dd38.png)\r\n\r\n可以看到 `Gas` 部分由以下部分组成:\r\n\r\n- `Gas Limit \u0026 Usage by Txn`: `GasLimit`和实际花费的`Gas`以及其在`GasLimit`中的占比，这笔交易中实际使用的比例达到了 98.83%。\r\n- `Gas Fees`\r\n    - `Base`: 基础的`GasPrice`\r\n    - `Max`: 最大 `GasPrice`\r\n    - `Max Priority`: 支付给以太坊节点矿工的 `GasPrice`\r\n- `Burnt \u0026 Txn Savings Fees`\r\n    - `Burnt`: 燃烧的手续费\r\n    - `Txn Savings`: 交易节省的费用\r\n\r\n`Txn Type: 2(EIP-1559)`: 根据 `EIP-2718` 明确交易类型为 2, 指明这是一笔 `EIP-1559` 的交易。\r\n\r\n## 什么是BaseFee?\r\n\r\n`BaseFee` 是 `EIP-1559` 提案中引入的一个机制，目的是改善以太坊的费用市场并提高用户体验。`BaseFee` 是每个区块的基础费用，它的目的是通过**自适应地调整费用**来反映网络的拥堵程度。\r\n\r\n源码中 `BaseFee`的计算如下：\r\n\r\n```go\r\nfunc CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {\r\n    // 首先,函数检查当前区块是否是第一个实施 EIP-1559 的区块。如果是,则返回初始基础费用。\r\n    if !config.IsLondon(parent.Number) {\r\n        return new(big.Int).SetUint64(params.InitialBaseFee)\r\n    }\r\n    // 如果不是,函数计算父区块的目标燃气量(gas target)。这是通过父区块的燃气限制除以弹性乘数得到的。\r\n    parentGasTarget := parent.GasLimit / config.ElasticityMultiplier()\r\n\t\r\n    // 如果父区块的实际燃气使用量等于目标燃气量,基础费用保持不变。\r\n    if parent.GasUsed == parentGasTarget {\r\n        return new(big.Int).Set(parent.BaseFee)\r\n    }\r\n\r\n    var (\r\n            num   = new(big.Int)\r\n            denom = new(big.Int)\r\n    )\r\n    \r\n    \r\n    if parent.GasUsed \u003e parentGasTarget {\r\n        // 如果父区块的燃气使用量大于目标值, 基础费用应该增加.\r\n        // max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)\r\n        num.SetUint64(parent.GasUsed - parentGasTarget)\r\n        num.Mul(num, parent.BaseFee)\r\n        num.Div(num, denom.SetUint64(parentGasTarget))\r\n        num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))\r\n        baseFeeDelta := math.BigMax(num, common.Big1)\r\n        \r\n        return num.Add(parent.BaseFee, baseFeeDelta)\r\n    } else {\r\n        // 如果父区块的燃气使用量小于目标值，基础费用应该减少\r\n        // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)\r\n        num.SetUint64(parentGasTarget - parent.GasUsed)\r\n        num.Mul(num, parent.BaseFee)\r\n        num.Div(num, denom.SetUint64(parentGasTarget))\r\n        num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))\r\n        baseFee := num.Sub(parent.BaseFee, num)\r\n\r\n        return math.BigMax(baseFee, common.Big0)\r\n\t}\r\n}\r\n```\r\n这个算法的目的是动态调整基础费用,以平衡网络使用。当网络拥堵时(燃气使用高),基础费用上升以抑制需求;当网络不繁忙时(燃气使用低),基础费用下降以鼓励使用。\r\n\r\n\r\n### 计算 `BaseFee` 过程如下:\r\n\r\n- 如果当前区块是第一个 `EIP-1559` 区块，则返回 `InitialBaseFee`, 其值为 `1 Gwei`\r\n- 计算 `parentGasTarget` 等于父区块 `GasLimit`除以弹性乘数得到的。\r\n\r\n大小关系就分为三种情况：\r\n\r\n- 父区块的 `GasUsed` 等于 `parentGasTarget`: `BaseFee` 不变\r\n- 父区块的 `GasUsed` 大于 `parentGasTarget`: `BaseFee` 增加, 计算遵循公式\r\n ```go\r\nparentBaseFee + max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)\r\n ```\r\n- 父区块的 `GasUsed` 小于 `parentGasTarget`: `BaseFee` 减少, 计算遵循公式\r\n ```go \r\nmax(0, parentBaseFee - parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)\r\n ```\r\n \r\n 其中：\r\n-   `GasUsed`: 实际使用的 `Gas`\r\n-   `parentBaseFee` 等于 父区块的 `BaseFee`\r\n-   `gasUsedDelta` 等于 `parent.GasUsed - parentGasTarget`，即父区块的 `Gas` 实际使用量与目标总量之间的差额\r\n-   `parentGasTarget` 父区块 `GasLimit` 的一半, 一般为 15000000\r\n-   `BaseFeeChangeDenominator` 常量，值为 8\r\n\r\n### 让我们根据真实的交易来验证下上面的计算公式：\r\n\r\n在etherscan[区块列表页](https://etherscan.io/blocks)有如下两个区块\r\n\r\n\r\n\r\n![Xnip2024-07-04_07-46-30.png](https://img.learnblockchain.cn/attachments/2024/07/x0bcrORE6685f70ee8759.png)\r\n\r\n\r\n在20229351区块中，我们看到\r\n\r\n- `BaseFee` 等于3.05Gwei\r\n- `gasUsedDelta / parentGasTarget` 等于 -23%, 说明需要降低 `BaseFee`\r\n\r\n按照公式计算20229352区块的 `BaseFee` 等于\r\n\r\n$$\r\nBaseFee=max(0, 3.05−3.05∗0.23/8) = 2.963 \r\n$$\r\n\r\n与图片一致\r\n\r\n\r\n## MaxPriorityFee\r\n\r\n`MaxPriorityFee` 是优先费用。是对每单位 `Gas` 的额外加价, 这部分的费用将支付给矿工，值越大则交易更快的被打包。通过[GasTracker](https://etherscan.io/gastracker)可查看当前最新的 `Gas` 信息\r\n\r\n最终的 `GasPrice` 等于 `BaseFee` 和 `MaxPriorityFee` 之和\r\n\r\n对于交易：\r\n\r\n\r\n![Xnip2024-07-03_18-54-31.png](https://img.learnblockchain.cn/attachments/2024/07/4OwvljBg6685f72559bdc.png)\r\n\r\n$\r\nTransaction Fee \r\n       = GasUsed∗GasPrice\r\n       =GasUsed∗(BaseFee+MaxPriorityFee)\r\n       =115855∗(7.407585749+0.05)\r\n       =863998.597Gwei\r\n$\r\n\r\n交易费用与图中 `Transaction Fee` 字段值一致\r\n\r\n## MaxFee\r\n\r\n`MaxFee` 意为最大的 `GasPrice`\r\n\r\n由于发送的交易不一定会在下一个区块内打包，而 `BaseFee` 又是在动态的改变，如果交易设置的 `MaxPriorityFee` 过低，则有可能交易不会被打包。只能等待后续区块的打包。但如果后续区块的 `BaseFee` 比之前的高，则会导致交易被丢弃。而设置较高的 `MaxFee`, 则可以保证交易在未来几个区块内不会因为 `BaseFee` 设置过低而被丢弃。\r\n\r\n还是以劳动力举例。现在每单位的劳动力的价格(`BaseFee`)是变动的, 由市场决定。在你发布一个工作后，而且还对每单位的劳动力付出额外报酬(`MaxPriorityFee`)的情况下，由于你愿意支付的报酬低于市场价，此时没人愿意为你工作，则会对你发布的工作进行下架处理。因此你给出了最大的每单位的劳动力价格(`MaxFee`)，只要当前的 `BaseFee` 加上 `MaxPriorityFee` 是小于 `MaxFee`，就可以继续招工。每单位劳动力价格仍按 `BaseFee + MaxPriorityFee`计算\r\n\r\n通常情况下 `MaxFee` 计算遵循公式:\r\n\r\n$$\r\nMaxFee=(2∗BaseFee)+MaxPriorityFee\r\n$$\r\n\r\n可以保证**连续** 6个区块满 `Gas` 的情况下仍在内存池中等待打包\r\n\r\n## Burnt\r\n\r\n燃烧的手续费, 即将这部分费用转入黑洞地址。转入数量由 `BaseFee` 决定\r\n\r\n$$\r\nBurnt=BaseFee∗GasUsed\r\n$$\r\n\r\n以上图中交易为例计算:\r\n\r\n$$\r\nBurnt=BaseFee∗GasUsed\r\n=7.407585749∗115855\r\n=858205.846950395 Gwei\r\n$$\r\n\r\n与图中 `Burnt` 字段值一致\r\n\r\n## Txn Savings\r\n\r\n交易节省的费用，等于最大可接受交易费用减去实际消耗的交易费用\r\n\r\n$$\r\nTxSavingsFees=MaxFee∗GasUsed−(BaseFee+MaxPriorityFee)∗GasUsed\r\n$$\r\n\r\n以上图中交易为例计算\r\n\r\n$\r\nTxSavingsFees=MaxFee∗GasUsed−(BaseFee+MaxPriorityFee)∗GasUsed\r\n=7.657591636∗115855−863998.597\r\n=23171.68198878Gwei\r\n$\r\n\r\n与图中 `Txn Savings` 字段值一致\r\n\r\n## JSON-RPC\r\n\r\n在发起 EIP1559 交易时, 常需要在交易中手动填入 `Gas` 相关的参数，这些参数可以通过向节点预发送 `http` 请求获得，包括了以下 `JSON-RPC` 方法\r\n\r\n-   `eth_estimateGas`\r\n-   `eth_maxPriorityFeePerGas`\r\n-   `eth_getBlockByNumber`\r\n\r\n### eth_estimateGas\r\n\r\n将交易发送到该接口，可以获得预估 `Gas`, 常用来设置交易的 `GasLimit`\r\n\r\n```json\r\n// Request Payload\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"method\": \"eth_estimateGas\",\r\n  \"params\": [\r\n    {\r\n      \"from\": \"0xD28C383dd3a1C0154129F67067175884e933cf4e\",\r\n      \"to\": \"0x7071D6EF9FaF45aA48c22bae7d4a295aD68DC038\",\r\n      \"value\": \"0x186a0\"\r\n    }\r\n  ],\r\n  \"id\": 1\r\n}\r\n// Response\r\n{\r\n  \"id\":1,\r\n  \"jsonrpc\": \"2.0\",\r\n  \"result\": \"0x5208\" // 21000\r\n}\r\n```\r\n\r\n### eth_maxPriorityFeePerGas\r\n\r\n该接口用来获取当前最新的 `MaxPriorityFee`\r\n\r\n```json\r\n// Request Payload\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"method\": \"eth_maxPriorityFeePerGas\",\r\n  \"params\": [],\r\n  \"id\": 1\r\n}\r\n// Response\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"result\": \"0x9b8495\", // MaxPriorityFee\r\n  \"id\": 1\r\n}\r\n```\r\n\r\n### eth_getBlockByNumber\r\n\r\n该接口用于获取区块信息，其中包含了 `BaseFee` 等信息\r\n```json\r\n// Request Payload\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"method\": \"eth_getBlockByNumber\",\r\n  \"params\": [\r\n    \"latest\",\r\n    false\r\n  ],\r\n  \"id\": 1\r\n}\r\n// Response\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"result\": {\r\n    \"baseFeePerGas\": \"0x1bc47470a\", // baseFee\r\n    \"difficulty\": \"0x0\",\r\n    \"extraData\": \"0x546974616e2028746974616e6275696c6465722e78797a29\",\r\n    \"gasLimit\": \"0x1c9c380\",\r\n    \"gasUsed\": \"0xced6fd\",\r\n    \"hash\": \"0xbb9b314d0b8208e655a0afc17384f56f44659a63e3ba4e244609105da497a7d9\",\r\n    ...\r\n  },\r\n  \"id\": 1\r\n}\r\n```\r\n\r\n获取最新的区块信息后, 字段 `baseFeePerGas` 的值就是 `BaseFee`。结合上面获取的 `MaxPriorityFee`, 可以用来设置 `MaxFee`\r\n\r\n```\r\nMax Fee = (2 * BaseFee) + MaxPriorityFee\r\n```\r\n\r\n## 参考资料\r\n[EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md)\r\n\r\n[EIP1559下的 GAS 费设置解析](https://learnblockchain.cn/article/3550)\r\n\r\n[以太坊 Gas 机制详解(EIP-1559)](https://learnblockchain.cn/article/6914)","title":"一篇文章彻底帮助你理解EIP1559之后的Gas机制"},"history":null,"timestamp":1720055660,"version":1}