快速构建以太坊 Gas 监控器:从 0 到 1 掌握 EIP-1559 手续费规律

·

在加密世界,以太坊 gas 价格像心跳一样起伏。伦敦升级(EIP-1559)将这趟「盲拍」升级成可预测的新机制:基础费 + 优先费
这两个关键词贯穿全文:以太坊手续费、实时监测、智能合约开发、区块链 API、EIP-1559、Web3、Alchemy、节点、React。

今天,我们亲手搭一套基于 ETH 主网的 Gas Tracker,帮你一眼看清最新 20 个区块的每笔费用构成与区块用量。最终你将上线一个浏览器实时刷新的仪表盘,基础费、优先费、区块填充率尽收眼底。

背景知识:为什么说 EIP-1559 是一次革命?

EIP-1559 的核心在于把费用拆分成两条轨道:

与此同步,区块 容量上限翻倍到 30M gas,但目标仍锁定在 15M,这样系统就能在高并发时弹性扩容,平稳后自动回落。
掌握这两点后再去阅读官方 EIP-1559 文档,你会发现任何一行公式都对应着你软件里的数据列。

如果对这些概念仍陌生,可先看这份中文速读:
👉 零基础速学 EIP-1559 关键细节,3 分钟掌握手续费新规则


第 1 部分:仅用 Node.js 就能跑起的脚本

我们将先把「历史 20 个区块」的 gas 费数据拉下来,再把监听脚本挂到新块事件里。所需工具全部开源,本地即可完成。

1. 环境准备

2. 三步创建项目

mkdir gas-tracker-script && cd gas-tracker-script
npm init -y
npm install --save @alch/alchemy-web3
touch main.js

3. 一行代码连主网

编辑 main.js,填入你的 WebSocket URL

const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(
  "wss://eth-mainnet.alchemyapi.io/v2/<--把 API Key 放进来-->"
);

4. 拉取历史 20 块的完整费用结构

const NUM_BLOCKS = 20;

const formatOutput = (data, n) => {
  let blocks = [];
  for (let i = 0; i < n; i++) {
    blocks.push({
      blockNumber: Number(data.oldestBlock) + i,
      baseFeePerGas: Math.round(Number(data.baseFeePerGas[i]) / 1e9),
      priorityFee25: Math.round(Number(data.reward[i][0]) / 1e9),
      priorityFee50: Math.round(Number(data.reward[i][1]) / 1e9),
      priorityFee75: Math.round(Number(data.reward[i][2]) / 1e9),
      gasUsedRatio: data.gasUsedRatio[i],
    });
  }
  return blocks;
};

web3.eth
  .getFeeHistory(NUM_BLOCKS, "latest", [25, 50, 75])
  .then((data) => console.log(formatOutput(data, NUM_BLOCKS)));

运行 node main.js 就能看到按时间顺序排好的 Gwei 单位数据。

5. 实时监听新块

把监听函数包进 subscribe('newBlockHeaders'),每隔 ~15 秒就会输出 最新的 20 块数据:

let sub = web3.eth.subscribe('newBlockHeaders');
sub.on('data', () => {
  web3.eth
    .getFeeHistory(NUM_BLOCKS, "latest", [25, 50, 75])
    .then((data) => console.log(formatOutput(data, NUM_BLOCKS)));
});

至此前置脚本完成,下面给它一张漂亮的「脸」。


第 2 部分:React 实时仪表盘(前端)

1. 初始化项目

npx create-react-app gas-tracker-app
cd gas-tracker-app
npm install --save @alch/alchemy-web3

2. 组件核心逻辑

App.js 直接替换为以下内容(已省去了繁琐样式,保留核心结构)。

import React, { useEffect, useState } from "react";
import { createAlchemyWeb3 } from "@alch/alchemy-web3";

const NUM_BLOCKS = 20;
function App() {
  const [blocks, setBlocks] = useState([]);
  const [avgGas, setAvgGas] = useState(0);
  const [avgVol, setAvgVol] = useState(0);

  useEffect(() => {
    const web3 = createAlchemyWeb3(
      "wss://eth-mainnet.alchemyapi.io/v2/<你的 API Key>"
    );

    const fetchData = () =>
      web3.eth
        .getFeeHistory(NUM_BLOCKS, "latest", [25, 50, 75])
        .then((data) => {
          let list = [];
          let totalGasFee = 0;
          let totalFill = 0;

          for (let i = 0; i < NUM_BLOCKS; i++) {
            const g = {
              blockNumber: Number(data.oldestBlock) + i,
              baseFee: Math.round(Number(data.baseFeePerGas[i]) / 1e9),
              priority25: Math.round(Number(data.reward[i][0]) / 1e9),
              priority50: Math.round(Number(data.reward[i][1]) / 1e9),
              priority75: Math.round(Number(data.reward[i][2]) / 1e9),
              fillRatio: Math.round(data.gasUsedRatio[i] * 100),
            };
            list.push(g);
            totalGasFee += g.baseFee + g.priority50;
            totalFill += g.fillRatio;
          }

          setBlocks(list);
          setAvgGas(Math.round(totalGasFee / NUM_BLOCKS));
          setAvgVol(Math.round(totalFill / NUM_BLOCKS));
        });

    fetchData();
    const sub = web3.eth.subscribe("newBlockHeaders");
    sub.on("data", fetchData);

    return () => web3.eth.clearSubscriptions();
  }, []);

  return (
    <div>
      <h2>EIP-1559 Gas Tracker</h2>
      <p>
        📊 最新 20 块平均手续费:<strong>{avgGas} Gwei</strong> | 平均区块填充率:<strong>{avgVol}%</strong>
      </p>

      <table>
        <thead>
          <tr>
            <th>区块号</th><th>基础费 (Gwei)</th><th>优先费 25%</th><th>优先费 50%</th><th>优先费 75%</th><th>填充率</th>
          </tr>
        </thead>
        <tbody>
          {blocks.map((b) => (
            <tr key={b.blockNumber}>
              <td>{b.blockNumber}</td>
              <td>{b.baseFee}</td>
              <td>{b.priority25}</td>
              <td>{b.priority50}</td>
              <td>{b.priority75}</td>
              <td>{b.fillRatio}%</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default App;
  1. 运行 npm start,浏览器 http://localhost:3000 里就能滚动监听最新区块了。

👉 想看代码跑起来的真实效果?立刻启动本地开发服务器试试


FAQ:你可能遇到的 6 个高频疑问

Q1:为什么基础费会忽高忽低却总是被烧掉?
A:它自动随区块饱/空 ±12.5 % 波动,且直接被合约地址 0x000...000 接收并销毁,防止矿工人为抬价,实现通胀控制。

Q2:优先费只要给得越多就一定更快吗?
A:并不。如果当前区块不拥堵,给多也会排队到下个空块;同时区块自身有容量上限。建议通过 25-75 % 分位统计指导出价即可。

Q3:脚本会在每次新块都重拉一次数据吗?耳机会占带宽?
A:WebSocket 每次只处理 20 行数据,流量极小;如要更省,可把 NUM_BLOCKS 调小。

Q4:Alchemy 调用次数有限制吗?
A:免费账号每天 100 K 次,个人练习绰绰有余;正式商用量大时再考虑付费套餐。

Q5:比别的星球链便宜就一定更好?
A:不同生态的算力与价值捕获不同。使用同一逻辑套用到 Polygon、BSC 只需换链名即可,但对应字段、精度及分位方法需微调。

Q6:我想把这份监控器上架到 Vercel?
A:把前端代码放到 GitHub,vercel --prod 一键部署。记住把 WebSocket URL 存成环境变量,别把 API Key 泄漏在浏览器里。


最终观察:数据为 EIP-1559 跑出了怎样的曲线?

假设今年用户总量增长 3 倍,只要区块用量不持续 >30 M,当前机制仍可维持稳定。
对应用开发者而言,能提前估算手续费 = 稳定 UX + 用户留存

👉 想继续深挖“链上手续费”新玩法?这里还有更酷的实时排行榜等你探索


你现在已经拥有一套可复用的 以太坊手续费监控系统,节点全部第三方托管,前端全部美化完成。把地址分享出去,旁边的朋友只需打开网页就能秒查“谁是现今最省 gas 的人”。下一次牛市蜂拥而至时,你已抢先掌握了 最透明、最可靠的链费数据来源。祝编码愉快!