Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 12 分钟

使用 eth_feeHistory 估算 Gas 价格:基础费用与优先费百分位数

学习读取 eth_feeHistory,并使用基础费用和优先费百分位数构建稳健的 EIP-1559 Gas 估算器。

TL;DR

伦敦分叉后,以太坊交易费用分为确定性的基础费用和可选的优先费。eth_feeHistory RPC 方法返回每个区块的基础费用,并在请求时返回包含交易的优先费百分位数。本文解释如何读取这些数据,并将其组合成稳健的 Gas 价格估算,避免过度支付或依赖单一的 eth_gasPrice 预言机。

EIP-1559 费用市场实践

自伦敦硬分叉以来,以太坊交易定价不再是单一的 gasPrice 拍卖。每个区块都有一个 baseFeePerGas,根据前一个区块的 Gas 使用情况算法确定。该基础费用被销毁,而非支付给矿工,并且每个区块最多可上下调整 12.5%,以保持区块 Gas 使用量接近目标。用户随后添加可选的 优先费(通常称为小费)以激励验证者包含其交易。总费用由您设置的 maxFeePerGas 上限,优先部分由 maxPriorityFeePerGas 上限。

官方 以太坊执行 API 规范 定义了 eth_feeHistory 方法,该方法返回一系列区块的基础费用和 Gas 使用率历史。当您请求 rewardPercentiles 时,客户端会扫描这些区块中包含的交易,并返回所请求百分位数的优先费。这是数据驱动 Gas 估算器的原始材料。

有关以太坊网络及其 RPC 端点的快速概述,请参阅 以太坊网络页面

读取 eth_feeHistory 响应

eth_feeHistory 方法接受三个参数:blockCount(要获取的区块数量)、newestBlock(最高区块号或标签 latest)和 rewardPercentiles(百分位数数组,例如 [25, 50, 75, 90])。响应包含三个关键字段:baseFeePerGas(每个区块的基础费用数组,外加下一个区块的额外一项)、gasUsedRatio(每个区块的 Gas 使用量与 Gas 限制的比率)和 reward(数组的数组,每个包含该区块在请求百分位数处的优先费)。

仅当您请求 rewardPercentiles 时,reward 字段才会被填充。如果省略,客户端返回空数组。此外,客户端计算这些百分位数的方式可能有所不同:有些扫描所有交易,有些采样,公共 RPC 提供商可能会限制历史深度甚至省略奖励数据以减少负载。这是因客户端和提供商而异的记录行为;始终使用您的特定端点进行测试。

baseFeePerGas 数组的元素比请求的区块数多一个。最后一个元素是假设 Gas 使用量不变的情况下,最新区块之后的下一个区块的基础费用。这对于预测下一个区块的基础费用很有用。

使用 Node.js 构建简单估算器

以下脚本调用 eth_feeHistory 获取最近 10 个区块,打印基础费用和 Gas 使用率,然后计算建议的 maxFeePerGasmaxPriorityFeePerGas 对。估算方法故意简单:它从历史记录中取第 90 百分位的优先费,并为平均基础费用增加 12.5% 的余量以应对可能的上涨。

您可以使用任何以太坊 RPC 端点运行此脚本,包括公共端点或您自己的节点。将 YOUR_RPC_URL 替换为您的端点。该脚本使用 Node.js 18+ 中可用的 fetch API。

const RPC_URL = 'YOUR_RPC_URL';

async function rpc(method, params) {
  const res = await fetch(RPC_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const data = await res.json();
  if (data.error) throw new Error(data.error.message);
  return data.result;
}

async function estimateGas() {
  const blockCount = 10;
  const newestBlock = 'latest';
  const rewardPercentiles = [25, 50, 75, 90];

  const history = await rpc('eth_feeHistory', [blockCount, newestBlock, rewardPercentiles]);

  console.log('Base fees per block:');
  console.log(history.baseFeePerGas);
  console.log('Gas used ratios:');
  console.log(history.gasUsedRatio);
  console.log('Reward percentiles per block:');
  console.log(history.reward);

  // Compute average base fee from the first blockCount entries (exclude the extra next-block estimate)
  const baseFees = history.baseFeePerGas.slice(0, blockCount).map(hex => parseInt(hex, 16));
  const avgBaseFee = baseFees.reduce((a, b) => a + b, 0) / baseFees.length;

  // Take the 90th percentile priority fee from the last block that has data
  const lastRewards = history.reward[history.reward.length - 1];
  const p90Priority = lastRewards ? parseInt(lastRewards[3], 16) : 0; // index 3 corresponds to 90th percentile

  // Add 12.5% headroom to base fee for possible increase
  const projectedBaseFee = Math.ceil(avgBaseFee * 1.125);
  const maxPriorityFeePerGas = p90Priority;
  const maxFeePerGas = projectedBaseFee + maxPriorityFeePerGas;

  console.log(`\nSuggested maxPriorityFeePerGas: ${maxPriorityFeePerGas}`);
  console.log(`Suggested maxFeePerGas: ${maxFeePerGas}`);
}

estimateGas().catch(console.error);

预期输出及如何解读

运行脚本时,您将看到类似以下的输出(值为示例,非测量值):

基础费用以 wei(十六进制)为单位。Gas 使用率告诉您每个区块的饱满程度:比率高于 0.5 意味着下一个区块的基础费用可能会增加,低于 0.5 则会降低。奖励百分位数显示每个百分位数处交易支付的优先费。例如,第 90 百分位值意味着 90% 的交易支付的小费等于或低于该值。

要验证估算器,您可以将自己的结果记录在如下表中:

区块范围平均基础费用 (Gwei)第 90 百分位小费 (Gwei)建议的 maxFee (Gwei)实际下一个区块基础费用 (Gwei)
10 个区块201.523.520.1
20 个区块192.023.419.8

Base fees per block:
["0x3b9aca00", "0x3b9aca00", ...]
Gas used ratios:
[0.5, 0.6, ...]
Reward percentiles per block:
[["0x3b9aca00", "0x4a817c80", ...], ...]

Suggested maxPriorityFeePerGas: 1000000000
Suggested maxFeePerGas: 2000000000

选择百分位数和时间范围

rewardPercentiles 的选择和要获取的区块数量取决于您对确认时间与成本的容忍度。常见的方法是使用第 25、50、75 和 90 百分位数。如果您需要快速包含,请使用第 90 百分位的小费;如果您愿意等待,第 50 百分位可能就足够了。

时间范围(blockCount)应反映近期的网络状况。短时间范围(例如 5-10 个区块)对变化反应迅速,但可能噪声较大。较长时间范围(例如 50-100 个区块)可以平滑短期峰值,但在拥堵期间可能滞后。对于大多数应用,10-20 个区块是合理的平衡。

请记住,基础费用每个区块最多可变化 12.5%。如果您发送的交易可能不会立即被挖掘,您应该根据预期的包含区块数来预测基础费用。一种简单的方法是取最近 N 个区块的平均基础费用并添加安全余量,如脚本所示。

避免重复计算基础费用

一个常见错误是将优先费百分位数加到当前基础费用上,并将其设置为 maxFeePerGas。然而,eth_feeHistory 报告的优先费是当时在基础费用之上支付的小费。如果基础费用此后上涨,那些历史交易实际支付的总费用更高,但您需要添加到预测基础费用上的只是小费。

换句话说,您的 maxFeePerGas 应该是预测的基础费用(含余量)加上您选择的优先费。优先费是您愿意支付的小费,与基础费用无关。这是为 EIP-1559 交易做预算的正确方式。

有关交易模拟和状态覆盖的更深入探讨,请参阅我们的指南 eth_call 状态覆盖与模拟

何时使用 eth_maxPriorityFeePerGas

许多客户端提供了一个便捷方法 eth_maxPriorityFeePerGas,根据近期网络状况返回建议的小费。这是一个快速捷径,但不包括基础费用。您仍然需要自己设置 maxFeePerGas,通常是将建议的小费添加到预测的基础费用上。

如果您不想自己实现百分位采样,使用 eth_maxPriorityFeePerGas 可以作为一个合理的回退,但它不太透明,可能无法反映您的特定包含要求。对于生产钱包,您可能会结合两者:使用 eth_feeHistory 进行基础费用预测,并使用 eth_maxPriorityFeePerGas 作为合理性检查。

局限性与权衡

基础费用不是包含的价格预言机。如果网络拥堵,低基础费用并不能保证您的交易会被包含;您仍然需要具有竞争力的优先费。此外,12.5% 的每区块变化限制适用于连续区块,但在多个区块中,基础费用可能大幅变动,因此您的预测应考虑这一点。

不同的交易类型需要不同的处理。类型 0 的传统交易使用单一的 gasPrice,涵盖基础费用和小费。类型 2 交易(EIP-1559)使用 maxFeePerGasmaxPriorityFeePerGas。如果您发送传统交易,则不能直接使用 eth_feeHistory;您需要估算一个足够高的 gasPrice 以覆盖基础费用加小费。

重组可能会改变您获取的历史数据。如果某个区块被重组,该区块的基础费用和奖励可能会发生变化。对于关键应用,您可能希望在依赖数据之前等待几个确认。

公共 RPC 提供商可能对 blockCount 有不同的限制,或者可能不支持所有网络上的 rewardPercentiles。某些网络(如某些 Layer 2)可能未实现 EIP-1559 或具有不同的费用机制。请始终查看您特定网络的文档。

常见 eth_feeHistory 问题排查

如果您遇到问题,以下是常见问题和解决方法:

  • 奖励字段为空:当您未请求 rewardPercentiles 或客户端/提供商不支持百分位采样时,会发生这种情况。尝试在请求中添加 rewardPercentiles,或使用其他 RPC 提供商。

  • HTTP 415 不支持的媒体类型:某些网络或提供商可能根本不支持 eth_feeHistory。请查看网络文档,或使用替代方法如 eth_gasPrice 作为回退。

  • blockCount 限制:某些提供商限制单次调用可请求的区块数量。如果出现错误,请减少 blockCount,并在需要时进行多次调用。

  • 十六进制与十进制:请记住,响应中的所有数值均为十六进制编码。使用 parseInt 并指定基数为 16 进行转换。

有关 RPC 可靠性的更多信息,请参阅我们的指南 以太坊 RPC 超时与重试以太坊 RPC 延迟与性能

后续步骤与进一步阅读

既然您了解了如何读取 eth_feeHistory,您可以构建一个更复杂的估算器,以适应网络状况。考虑将其集成到您的钱包或中继中,以减少过度支付。有关以太坊 RPC 的更广泛理解,请探索 OnFinality 学习中心API 服务 文档。

如果您正在选择以太坊节点提供商,我们的 选择以太坊 RPC 节点指南 可以帮助您评估诸如 eth_feeHistory 支持等功能。此外,请查看 RPC 定价 以了解高频调用的成本影响。

有关相关主题,请参阅我们的指南 使用 eth_getTransactionCount 管理 EVM nonceeth_call 状态覆盖与模拟

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始