伦敦分叉后,以太坊交易费用分为确定性的基础费用和可选的优先费。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 使用率,然后计算建议的 maxFeePerGas 和 maxPriorityFeePerGas 对。估算方法故意简单:它从历史记录中取第 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 个区块 | 20 | 1.5 | 23.5 | 20.1 |
| 20 个区块 | 19 | 2.0 | 23.4 | 19.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)使用 maxFeePerGas 和 maxPriorityFeePerGas。如果您发送传统交易,则不能直接使用 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 nonce 和 eth_call 状态覆盖与模拟。